Tests/Src/Lab.Tests.ps1
#requires -RunAsAdministrator #requires -Version 4 $moduleName = 'Lability'; $repoRoot = (Resolve-Path "$PSScriptRoot\..\..").Path; Import-Module (Join-Path -Path $RepoRoot -ChildPath "$moduleName.psm1") -Force; Describe 'Src\Lab' { InModuleScope $moduleName { Context 'Validates "Start-Lab" method' { It 'Starts all VMs with matching boot order' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } Mock Start-VM -MockWith { } Mock Start-Sleep -MockWith { } Start-Lab -ConfigurationData $configurationData; Assert-MockCalled Start-VM -Exactly 1 -Scope It; } It 'Starts all VMs with differing boot orders individually' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; BootOrder = 1; }, @{ NodeName = 'VM2'; BootOrder = 2; }, @{ NodeName = 'VM3'; BootOrder = 3; } ) } Mock Start-VM -MockWith { } Mock Start-Sleep -MockWith { } Start-Lab -ConfigurationData $configurationData; Assert-MockCalled Start-VM -Exactly ($configurationData.AllNodes).Count -Scope It; } It 'Starts VM using display name' { $testVMName = 'VM1'; $testEnvironmentPrefix = 'Test_'; $testVMDisplayName = '{0}{1}' -f $testEnvironmentPrefix, $testVMName; $configurationData = @{ AllNodes = @( @{ NodeName = $testVMName; } ) NonNodeData = @{ Lability = @{ EnvironmentPrefix = $testEnvironmentPrefix; } } } Mock Start-VM -ParameterFilter { $Name -eq $testVMDisplayName } -MockWith { } Mock Start-Sleep -MockWith { } Start-Lab -ConfigurationData $configurationData; Assert-MockCalled Start-VM -ParameterFilter { $Name -eq $testVMDisplayName } -Scope It; } It 'Starts VMs in boot order' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; BootOrder = 2; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; BootOrder = 1; } # Defaults to 99 ) } Mock Start-VM -MockWith { Write-Output $Name; } Mock Start-Sleep -MockWith { } $actualStartOrder = Start-Lab -ConfigurationData $configurationData; $actualStartOrder[0] | Should Be 'VM3'; $actualStartOrder[1] | Should Be 'VM1'; $actualStartOrder[2] | Should Be 'VM2'; } It 'Does not call "Start-Sleep" if a zero boot delay is specified' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM3'; BootDelay = 0; } # Defaults to a delay of 5 seconds ) } Mock Start-VM -MockWith { } Mock Start-Sleep -MockWith { } Start-Lab -ConfigurationData $configurationData; Assert-MockCalled Start-Sleep -Exactly ($configurationData.AllNodes | Where { $_.BootDelay -ne $null -and $_.BootDelay -gt 0 }).Count -Scope It; } } #end context Validates "Start-Lab" method Context 'Validates "Stop-Lab" method' { It 'Stops all VMs with matching boot order' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } Mock Stop-VM -MockWith { } Mock Start-Sleep -MockWith { } Stop-Lab -ConfigurationData $configurationData; Assert-MockCalled Stop-VM -Exactly 1 -Scope It; } It 'Stops all VMs with differing boot orders individually' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; BootOrder = 1; }, @{ NodeName = 'VM2'; BootOrder = 2; }, @{ NodeName = 'VM3'; BootOrder = 3; } ) } Mock Stop-VM -MockWith { } Mock Start-Sleep -MockWith { } Stop-Lab -ConfigurationData $configurationData; Assert-MockCalled Stop-VM -Exactly ($configurationData.AllNodes).Count -Scope It; } It 'Stops VM using display name' { $testVMName = 'VM1'; $testEnvironmentPrefix = 'Test_'; $testVMDisplayName = '{0}{1}' -f $testEnvironmentPrefix, $testVMName; $configurationData = @{ AllNodes = @( @{ NodeName = $testVMName; } ) NonNodeData = @{ Lability = @{ EnvironmentPrefix = $testEnvironmentPrefix; } } } Mock Stop-VM -ParameterFilter { $Name -eq $testVMDisplayName } -MockWith { } Mock Start-Sleep -MockWith { } Stop-Lab -ConfigurationData $configurationData; Assert-MockCalled Stop-VM -ParameterFilter { $Name -eq $testVMDisplayName } -Scope It; } It 'Stops VMs in boot order' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; BootOrder = 2; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; BootOrder = 1; } # Defaults to 99 ) } Mock Stop-VM -MockWith { Write-Output $Name; } Mock Start-Sleep -MockWith { } $actualStopOrder = Stop-Lab -ConfigurationData $configurationData; $actualStopOrder[0] | Should Be 'VM2'; $actualStopOrder[1] | Should Be 'VM1'; $actualStopOrder[2] | Should Be 'VM3'; } It 'Calls "Stop-VM" with "Force"' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; } ) } Mock Stop-VM -ParameterFilter { $Force -eq $true } -MockWith { } Mock Start-Sleep -MockWith { } Stop-Lab -ConfigurationData $configurationData; Assert-MockCalled Stop-VM -ParameterFilter { $Force -eq $true } -Scope It; } It 'Does not call "Start-Sleep" if a zero boot delay is specified' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM3'; BootDelay = 0; } # Defaults to a delay of 5 seconds ) } Mock Stop-VM -MockWith { } Mock Start-Sleep -MockWith { } Stop-Lab -ConfigurationData $configurationData; Assert-MockCalled Start-Sleep -Exactly ($configurationData.AllNodes | Where { $_.BootDelay -ne $null -and $_.BootDelay -gt 0 }).Count -Scope It; } } #end context Validates "Stop-Lab" method Context 'Validates "Reset-Lab" method' { It 'Calls "Restore-Lab" with -Force switch' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } Mock Restore-Lab -ParameterFilter { $Force -eq $true } -MockWith { } Reset-Lab -ConfigurationData $configurationData; Assert-MockCalled Restore-Lab -ParameterFilter { $Force -eq $true } -Scope It; } } #end context Validates "Reset-Lab" method Context 'Validates "Checkpoint-Lab" method' { It 'Snapshots VMs when powered off' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; Mock Get-VM -MockWith { return [PSCustomObject] @{ State = 'Off' }; } Mock NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } Checkpoint-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName; Assert-MockCalled NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName }; } It 'Snapshots VM using display name' { $testVMName = 'VM1'; $testEnvironmentPrefix = 'Test_'; $testVMDisplayName = '{0}{1}' -f $testEnvironmentPrefix, $testVMName; $configurationData = @{ AllNodes = @( @{ NodeName = $testVMName; } ) NonNodeData = @{ Lability = @{ EnvironmentPrefix = $testEnvironmentPrefix; } } } $testSnapshotName = 'Test Snapshot'; Mock Get-VM -MockWith { return [PSCustomObject] @{ State = 'Off' }; } Mock NewLabVMSnapshot -ParameterFilter { $Name -eq $testVMDisplayName } -MockWith { } Checkpoint-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName; Assert-MockCalled NewLabVMSnapshot -ParameterFilter { $Name -eq $testVMDisplayName }; } It 'Errors when there is one running VM' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; $fakeRunningVM = [PSCustomObject] @{ State = 'Running' }; Mock Get-VM -MockWith { return @($fakeRunningVM, $fakeOffVM, $fakeOffVM); } Mock NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } { Checkpoint-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName -ErrorAction Stop } | Should Throw; } It 'Errors when there are multiple running VMs' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; $fakeRunningVM = [PSCustomObject] @{ State = 'Running' }; Mock Get-VM -MockWith { return @($fakeRunningVM, $fakeOffVM, $fakeRunningVM); } Mock NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } { Checkpoint-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName -ErrorAction Stop } | Should Throw; } It 'Snapshots VMs when there are running VMs and -Force is specified' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; $fakeRunningVM = [PSCustomObject] @{ State = 'Running' }; Mock Get-VM -MockWith { return @($fakeRunningVM, $fakeOffVM, $fakeOffVM); } Mock NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } Checkpoint-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName -Force; Assert-MockCalled NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName }; } } #end context Validates "Checkpoint-Lab" method Context 'Validates "Restore-Lab" method' { It 'Restores specified snapshot when VMs are powered off' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; Mock Get-VM -MockWith { return [PSCustomObject] @{ Name = $Name; State = 'Off'; } } Mock Restore-VMSnapshot -MockWith { } #TODO: Cannot mock pipeline input to Restore-VMSnapshot? Mock GetLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } Restore-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName; Assert-MockCalled GetLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -Scope It; } It 'Restores VM snapshot using display name' { $testVMName = 'VM1'; $testEnvironmentPrefix = 'Test_'; $testVMDisplayName = '{0}{1}' -f $testEnvironmentPrefix, $testVMName; $configurationData = @{ AllNodes = @( @{ NodeName = $testVMName; } ) NonNodeData = @{ Lability = @{ EnvironmentPrefix = $testEnvironmentPrefix; } } } $testSnapshotName = 'Test Snapshot'; Mock Get-VM -MockWith { return [PSCustomObject] @{ State = 'Off' }; } Mock Restore-VMSnapshot -MockWith { } #TODO: Cannot mock pipeline input to Restore-VMSnapshot? Mock GetLabVMSnapshot -ParameterFilter { $Name -eq $testVMDisplayName } -MockWith { } Restore-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName; Assert-MockCalled GetLabVMSnapshot -ParameterFilter { $Name -eq $testVMDisplayName }; } It 'Errors when there is a running VM' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; $fakeRunningVM = [PSCustomObject] @{ State = 'Running' }; Mock Get-VM -MockWith { return @($fakeRunningVM, $fakeOffVM, $fakeOffVM); } Mock NewLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } { Restore-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName -ErrorAction Stop } | Should Throw; } It 'Restores specified snapshot when there are running VMs and -Force is specified' { $configurationData = @{ AllNodes = @( @{ NodeName = 'VM1'; }, @{ NodeName = 'VM2'; }, @{ NodeName = 'VM3'; } ) } $testSnapshotName = 'Test Snapshot'; $fakeOffVM = [PSCustomObject] @{ State = 'Off' }; $fakeRunningVM = [PSCustomObject] @{ State = 'Running' }; Mock Get-VM -MockWith { return @($fakeRunningVM, $fakeOffVM, $fakeOffVM); } Mock GetLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -MockWith { } Restore-Lab -ConfigurationData $configurationData -SnapshotName $testSnapshotName -Force; Assert-MockCalled GetLabVMSnapshot -ParameterFilter { $SnapshotName -eq $testSnapshotName } -Scope It; } } #end context Validates "Restore-Lab" method } #end InModuleScope } #end describe Src\Lab |