tests/Public/Start-CloudInstance.Tests.ps1
|
BeforeAll { Import-Module (Resolve-Path (Join-Path $PSScriptRoot '..\..\PSCumulus.psd1')).Path -Force . (Join-Path $PSScriptRoot 'TestHelpers.ps1') } Describe 'Start-CloudInstance' { Context 'parameter validation' { It 'makes Provider optional in every parameter set' { foreach ($parameterSet in 'Azure', 'AWS', 'GCP') { Should-HaveOptionalParameter ` -CommandName 'Start-CloudInstance' ` -ParameterSetName $parameterSet ` -ParameterName 'Provider' } } It 'requires -Name and -ResourceGroup in the Azure parameter set' { Should-HaveMandatoryParameters ` -CommandName 'Start-CloudInstance' ` -ParameterSetName 'Azure' ` -ParameterNames @('Name', 'ResourceGroup') } It 'requires -InstanceId in the AWS parameter set' { Should-HaveMandatoryParameter ` -CommandName 'Start-CloudInstance' ` -ParameterSetName 'AWS' ` -ParameterName 'InstanceId' } It 'requires -Name, -Zone, and -Project in the GCP parameter set' { Should-HaveMandatoryParameters ` -CommandName 'Start-CloudInstance' ` -ParameterSetName 'GCP' ` -ParameterNames @('Name', 'Zone', 'Project') } It 'rejects an invalid provider name' { { Start-CloudInstance -Provider Oracle -Name 'vm' -ResourceGroup 'rg' } | Should -Throw } } Context 'Azure routing' { It 'calls Start-AzureInstance for Azure provider' { InModuleScope PSCumulus { Mock Start-AzureInstance { [AzureCloudRecord]@{ Name = 'vm01'; Provider = 'Azure'; Status = 'Starting' } } Start-CloudInstance -Provider Azure -Name 'vm01' -ResourceGroup 'prod-rg' Should -Invoke Start-AzureInstance -Times 1 } } It 'passes Name and ResourceGroup to the Azure backend' { InModuleScope PSCumulus { Mock Start-AzureInstance { param([string]$Name, [string]$ResourceGroup) [AzureCloudRecord]@{ Name = $Name; Provider = 'Azure'; Metadata = @{ RG = $ResourceGroup } } } $result = Start-CloudInstance -Provider Azure -Name 'my-vm' -ResourceGroup 'my-rg' $result.Name | Should -Be 'my-vm' $result.Metadata.RG | Should -Be 'my-rg' } } It 'infers Azure when Provider is omitted' { InModuleScope PSCumulus { Mock Start-AzureInstance { [AzureCloudRecord]@{ Name = 'vm01'; Provider = 'Azure'; Status = 'Starting' } } Start-CloudInstance -Name 'vm01' -ResourceGroup 'prod-rg' Should -Invoke Start-AzureInstance -Times 1 } } It 'accepts piped Azure instance records' { InModuleScope PSCumulus { Mock Start-AzureInstance { param([string]$Name, [string]$ResourceGroup) [AzureCloudRecord]@{ Name = $Name; Provider = 'Azure'; Metadata = @{ RG = $ResourceGroup } } } $inputRecord = [AzureCloudRecord]@{ Name = 'web-server-01'; Provider = 'Azure'; ResourceGroup = 'prod-rg' } $result = $inputRecord | Start-CloudInstance -Confirm:$false $result.Name | Should -Be 'web-server-01' $result.Metadata.RG | Should -Be 'prod-rg' } } } Context 'AWS routing' { It 'calls Start-AWSInstance for AWS provider' { InModuleScope PSCumulus { Mock Start-AWSInstance { [AWSCloudRecord]@{ Name = 'i-abc'; Provider = 'AWS'; Status = 'Starting' } } Start-CloudInstance -Provider AWS -InstanceId 'i-abc' -Region 'us-east-1' Should -Invoke Start-AWSInstance -Times 1 } } It 'passes InstanceId to the AWS backend' { InModuleScope PSCumulus { Mock Start-AWSInstance { param([string]$InstanceId) [AWSCloudRecord]@{ Name = $InstanceId; Provider = 'AWS'; Status = 'Starting' } } $result = Start-CloudInstance -Provider AWS -InstanceId 'i-0abc123' $result.Name | Should -Be 'i-0abc123' } } It 'infers AWS when Provider is omitted' { InModuleScope PSCumulus { Mock Start-AWSInstance { [AWSCloudRecord]@{ Name = 'i-abc'; Provider = 'AWS'; Status = 'Starting' } } Start-CloudInstance -InstanceId 'i-abc' -Region 'us-east-1' Should -Invoke Start-AWSInstance -Times 1 } } It 'accepts piped AWS instance records' { InModuleScope PSCumulus { Mock Start-AWSInstance { param([string]$InstanceId, [string]$Region) [AWSCloudRecord]@{ Name = $InstanceId; Provider = 'AWS'; Region = $Region; Status = 'Starting' } } $inputRecord = [AWSCloudRecord]@{ Name = 'app-server-01'; Provider = 'AWS'; InstanceId = 'i-0abc123' } $result = $inputRecord | Start-CloudInstance -Confirm:$false $result.Name | Should -Be 'i-0abc123' $result.Region | Should -BeNullOrEmpty } } } Context 'GCP routing' { It 'calls Start-GCPInstance for GCP provider' { InModuleScope PSCumulus { Mock Start-GCPInstance { [GCPCloudRecord]@{ Name = 'gcp-vm'; Provider = 'GCP'; Status = 'Starting' } } Start-CloudInstance -Provider GCP -Name 'gcp-vm' -Zone 'us-central1-a' -Project 'my-project' Should -Invoke Start-GCPInstance -Times 1 } } It 'passes Name, Zone, and Project to the GCP backend' { InModuleScope PSCumulus { Mock Start-GCPInstance { param([string]$Name, [string]$Zone, [string]$Project) [GCPCloudRecord]@{ Name = $Name; Provider = 'GCP'; Region = $Zone; Metadata = @{ Proj = $Project } } } $result = Start-CloudInstance -Provider GCP -Name 'gcp-vm' -Zone 'us-central1-a' -Project 'prod-gcp' $result.Name | Should -Be 'gcp-vm' $result.Region | Should -Be 'us-central1-a' $result.Metadata.Proj | Should -Be 'prod-gcp' } } It 'accepts piped GCP instance records' { InModuleScope PSCumulus { Mock Start-GCPInstance { param([string]$Name, [string]$Zone, [string]$Project) [GCPCloudRecord]@{ Name = $Name; Provider = 'GCP'; Region = $Zone; Metadata = @{ Proj = $Project } } } $inputRecord = [GCPCloudRecord]@{ Name = 'gcp-vm'; Provider = 'GCP'; Region = 'us-central1-a'; Project = 'prod-gcp'; Zone = 'us-central1-a' } $result = $inputRecord | Start-CloudInstance -Confirm:$false $result.Name | Should -Be 'gcp-vm' $result.Region | Should -Be 'us-central1-a' $result.Metadata.Proj | Should -Be 'prod-gcp' } } } Context 'Path parameter set' { It 'dispatches Azure path to Start-AzureInstance with correct parameters' { InModuleScope PSCumulus { Mock Start-AzureInstance { param([string]$Name, [string]$ResourceGroup) [AzureCloudRecord]@{ Name = $Name; Provider = 'Azure'; Metadata = @{ RG = $ResourceGroup } } } Start-CloudInstance -Path 'Azure:\prod-rg\Instances\web-server-01' -Confirm:$false Should -Invoke Start-AzureInstance -Times 1 -ParameterFilter { $Name -eq 'web-server-01' -and $ResourceGroup -eq 'prod-rg' } } } It 'dispatches AWS path to Start-AWSInstance with correct parameters' { InModuleScope PSCumulus { Mock Start-AWSInstance { param([string]$InstanceId, [string]$Region) [AWSCloudRecord]@{ Name = $InstanceId; Provider = 'AWS'; Region = $Region; Status = 'Starting' } } Start-CloudInstance -Path 'AWS:\us-east-1\Instances\i-12345678' -Confirm:$false Should -Invoke Start-AWSInstance -Times 1 -ParameterFilter { $InstanceId -eq 'i-12345678' -and $Region -eq 'us-east-1' } } } It 'dispatches GCP path and resolves Zone via Get-GCPInstanceData lookup' { InModuleScope PSCumulus { Mock Get-GCPInstanceData { [GCPCloudRecord]@{ Name = 'gcp-vm'; Provider = 'GCP'; Zone = 'us-central1-a'; Project = 'my-project' } } Mock Start-GCPInstance { param([string]$Name, [string]$Zone, [string]$Project) [GCPCloudRecord]@{ Name = $Name; Provider = 'GCP'; Region = $Zone; Metadata = @{ Proj = $Project } } } Start-CloudInstance -Path 'GCP:\my-project\Instances\gcp-vm' -Confirm:$false Should -Invoke Get-GCPInstanceData -Times 1 -ParameterFilter { $Project -eq 'my-project' -and $Name -eq 'gcp-vm' } Should -Invoke Start-GCPInstance -Times 1 -ParameterFilter { $Name -eq 'gcp-vm' -and $Zone -eq 'us-central1-a' -and $Project -eq 'my-project' } } } It 'throws ArgumentException for non-Resource depth paths' { InModuleScope PSCumulus { { Start-CloudInstance -Path 'Azure:\prod-rg\Instances' } | Should -Throw -ExpectedMessage '*Path must resolve to a specific resource*' { Start-CloudInstance -Path 'Azure:\prod-rg' } | Should -Throw -ExpectedMessage '*Path must resolve to a specific resource*' { Start-CloudInstance -Path 'Azure:\' } | Should -Throw -ExpectedMessage '*Path must resolve to a specific resource*' } } It 'throws ArgumentException for non-Instances kind' { InModuleScope PSCumulus { { Start-CloudInstance -Path 'Azure:\prod-rg\Disks\disk-01' } | Should -Throw -ExpectedMessage '*only supported for Instances*' { Start-CloudInstance -Path 'AWS:\us-east-1\Storage\bucket' } | Should -Throw -ExpectedMessage '*only supported for Instances*' } } It 'does not invoke backend when -WhatIf is used' { InModuleScope PSCumulus { Mock Start-AzureInstance { [AzureCloudRecord]@{ Name = 'vm01'; Provider = 'Azure'; Status = 'Starting' } } Start-CloudInstance -Path 'Azure:\prod-rg\Instances\web-server-01' -WhatIf Should -Invoke Start-AzureInstance -Times 0 } } It 'throws InvalidOperationException when GCP instance not found' { InModuleScope PSCumulus { Mock Get-GCPInstanceData { return $null } { Start-CloudInstance -Path 'GCP:\my-project\Instances\nonexistent-vm' } | Should -Throw -ExpectedMessage '*not found in project*' } } } } |