Tests/security/Get-RandomPassword.Tests.ps1
|
#Requires -Version 5.1 #Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } BeforeAll { # Import module $script:modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\PSWinOps.psd1' Import-Module -Name $script:modulePath -Force -ErrorAction Stop # Define test data $script:defaultLength = 16 $script:defaultUpperCount = 2 $script:defaultLowerCount = 2 $script:defaultNumericCount = 2 $script:defaultSpecialCount = 2 $script:upperCharSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' $script:lowerCharSet = 'abcdefghijklmnopqrstuvwxyz' $script:numericCharSet = '0123456789' $script:specialCharSet = '@.+-=*!#$%&?' } Describe -Name 'Get-RandomPassword' -Fixture { Context -Name 'Module integration' -Fixture { It -Name 'Should be available after module import' -Test { Get-Command -Name 'Get-RandomPassword' -Module 'PSWinOps' -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty } It -Name 'Should have correct OutputType attribute' -Test { $command = Get-Command -Name 'Get-RandomPassword' $command.OutputType.Name | Should -Contain 'System.String' } } Context -Name 'Parameter validation' -Fixture { It -Name 'Should reject length less than 8' -Test { { Get-RandomPassword -Length 7 } | Should -Throw } It -Name 'Should reject negative count values' -Test { { Get-RandomPassword -UpperCount -1 } | Should -Throw } It -Name 'Should throw when total constraints exceed length' -Test { { Get-RandomPassword -Length 10 -UpperCount 5 -LowerCount 5 -NumericCount 5 -SpecialCount 5 } | Should -Throw -ExpectedMessage '*exceeds password length*' } It -Name 'Should throw when all character class counts are zero' -Test { { Get-RandomPassword -UpperCount 0 -LowerCount 0 -NumericCount 0 -SpecialCount 0 } | Should -Throw -ExpectedMessage '*at least one character class*' } } Context -Name 'Password generation with default parameters' -Fixture { It -Name 'Should return a string' -Test { $result = Get-RandomPassword $result | Should -BeOfType ([string]) } It -Name 'Should return password of default length 16' -Test { $result = Get-RandomPassword $result.Length | Should -Be 16 } It -Name 'Should contain at least 2 uppercase characters' -Test { $result = Get-RandomPassword $upperCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:upperCharSet.ToCharArray() }).Count $upperCount | Should -BeGreaterOrEqual 2 } It -Name 'Should contain at least 2 lowercase characters' -Test { $result = Get-RandomPassword $lowerCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:lowerCharSet.ToCharArray() }).Count $lowerCount | Should -BeGreaterOrEqual 2 } It -Name 'Should contain at least 2 numeric characters' -Test { $result = Get-RandomPassword $numericCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:numericCharSet.ToCharArray() }).Count $numericCount | Should -BeGreaterOrEqual 2 } It -Name 'Should contain at least 2 special characters' -Test { $result = Get-RandomPassword $specialCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:specialCharSet.ToCharArray() }).Count $specialCount | Should -BeGreaterOrEqual 2 } } Context -Name 'Password generation with custom parameters' -Fixture { It -Name 'Should return password of specified length' -Test { $result = Get-RandomPassword -Length 24 $result.Length | Should -Be 24 } It -Name 'Should meet custom uppercase requirement' -Test { $result = Get-RandomPassword -Length 20 -UpperCount 5 $upperCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:upperCharSet.ToCharArray() }).Count $upperCount | Should -BeGreaterOrEqual 5 } It -Name 'Should generate password with zero special characters when SpecialCount is 0' -Test { $result = Get-RandomPassword -Length 16 -UpperCount 4 -LowerCount 4 -NumericCount 4 -SpecialCount 0 $specialCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:specialCharSet.ToCharArray() }).Count $specialCount | Should -Be 0 } It -Name 'Should generate password with only numeric characters' -Test { $result = Get-RandomPassword -Length 12 -UpperCount 0 -LowerCount 0 -NumericCount 12 -SpecialCount 0 $result | Should -Match '^\d+$' } } Context -Name 'Uniqueness and randomness' -Fixture { It -Name 'Should generate different passwords on consecutive calls' -Test { $password1 = Get-RandomPassword -Length 20 $password2 = Get-RandomPassword -Length 20 $password1 | Should -Not -Be $password2 } It -Name 'Should generate 10 unique passwords' -Test { $passwords = 1..10 | ForEach-Object { Get-RandomPassword -Length 16 } $uniquePasswords = $passwords | Select-Object -Unique $uniquePasswords.Count | Should -Be 10 } } Context -Name 'Error handling and edge cases' -Fixture { It -Name 'Should throw when constraints exceed length' -Test { { Get-RandomPassword -Length 8 -UpperCount 8 -LowerCount 2 -NumericCount 2 -SpecialCount 2 } | Should -Throw -ExpectedMessage '*exceeds password length*' } It -Name 'Should throw when constraints are mathematically impossible' -Test { # Impossible: 8 upper + 1 lower + 1 numeric = 10 total required > 8 length { Get-RandomPassword -Length 8 -UpperCount 8 -LowerCount 1 -NumericCount 1 -SpecialCount 0 } | Should -Throw -ExpectedMessage '*exceeds password length*' } It -Name 'Should generate successfully with tight but valid constraints' -Test { $result = Get-RandomPassword -Length 8 -UpperCount 2 -LowerCount 2 -NumericCount 2 -SpecialCount 2 $result.Length | Should -Be 8 # Verify all constraints are met $upperCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:upperCharSet.ToCharArray() }).Count $lowerCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:lowerCharSet.ToCharArray() }).Count $numericCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:numericCharSet.ToCharArray() }).Count $specialCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:specialCharSet.ToCharArray() }).Count $upperCount | Should -BeGreaterOrEqual 2 $lowerCount | Should -BeGreaterOrEqual 2 $numericCount | Should -BeGreaterOrEqual 2 $specialCount | Should -BeGreaterOrEqual 2 } It -Name 'Should generate successfully with exact constraint match to length' -Test { # Length = 12, constraints = 3+3+3+3 = 12 (no extra random characters) $result = Get-RandomPassword -Length 12 -UpperCount 3 -LowerCount 3 -NumericCount 3 -SpecialCount 3 $result.Length | Should -Be 12 # Verify exact counts $upperCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:upperCharSet.ToCharArray() }).Count $lowerCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:lowerCharSet.ToCharArray() }).Count $numericCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:numericCharSet.ToCharArray() }).Count $specialCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:specialCharSet.ToCharArray() }).Count $upperCount | Should -Be 3 $lowerCount | Should -Be 3 $numericCount | Should -Be 3 $specialCount | Should -Be 3 } It -Name 'Should handle minimum length password' -Test { $result = Get-RandomPassword -Length 8 -UpperCount 2 -LowerCount 2 -NumericCount 2 -SpecialCount 2 $result.Length | Should -Be 8 $result | Should -BeOfType ([string]) } It -Name 'Should handle large password generation' -Test { $result = Get-RandomPassword -Length 128 -UpperCount 20 -LowerCount 20 -NumericCount 20 -SpecialCount 20 $result.Length | Should -Be 128 $upperCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:upperCharSet.ToCharArray() }).Count $lowerCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:lowerCharSet.ToCharArray() }).Count $numericCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:numericCharSet.ToCharArray() }).Count $specialCount = ($result.ToCharArray() | Where-Object { $_ -cin $script:specialCharSet.ToCharArray() }).Count $upperCount | Should -BeGreaterOrEqual 20 $lowerCount | Should -BeGreaterOrEqual 20 $numericCount | Should -BeGreaterOrEqual 20 $specialCount | Should -BeGreaterOrEqual 20 } } Context -Name 'Verbose output' -Fixture { It -Name 'Should produce verbose output when -Verbose is specified' -Test { $verboseOutput = Get-RandomPassword -Length 16 -Verbose 4>&1 $verboseOutput | Should -Not -BeNullOrEmpty $verboseOutput -join ' ' | Should -Match 'Get-RandomPassword.*Starting password generation|Get-RandomPassword.*Character set size' } } } AfterAll { Remove-Module -Name 'PSWinOps' -Force -ErrorAction SilentlyContinue } |