Tests/Public/Mount-FileShare.Tests.ps1
|
BeforeAll { $modulePath = "$PSScriptRoot/../.." # Dot-source all private and public functions for testing Get-ChildItem -Path "$modulePath/Private/*.ps1" | ForEach-Object { . $_.FullName } Get-ChildItem -Path "$modulePath/Public/*.ps1" | ForEach-Object { . $_.FullName } } Describe 'Mount-FileShare' { BeforeEach { Mock Write-Host {} Mock Write-Warning {} Mock Write-Verbose {} Mock Test-Path { $false } Mock Invoke-NetUse { $global:LASTEXITCODE = 0 'OK' } Mock Invoke-CmdKey { '' } } Context 'Multiple shares' { It 'Mounts all shares defined in ShareDriveMap' { $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) $map = @{ 'data' = 'D'; 'apps' = 'A'; 'files' = 'F' } Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap $map -Confirm:$false Should -Invoke Invoke-NetUse -Times 3 } } Context 'Custom share map' { It 'Mounts only specified shares' { $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D'; 'apps' = 'A' } -Confirm:$false Should -Invoke Invoke-NetUse -Times 2 } } Context 'Username expansion' { It 'Replaces %USERNAME% token with credential username prefix' { $cred = [PSCredential]::new('jdoe@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mock Invoke-NetUse { $global:LASTEXITCODE = 0 'OK' } -Verifiable Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'staff/%USERNAME%' = 'H' } -Confirm:$false # Verify Invoke-NetUse was called (the UNC path expansion happens internally) Should -Invoke Invoke-NetUse -Times 1 } } Context 'When mount fails' { It 'Logs a warning and continues with remaining shares' { Mock Invoke-NetUse { $global:LASTEXITCODE = 1; 'System error 53' } $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'a' = 'A'; 'b' = 'B' } -Confirm:$false Should -Invoke Write-Warning -Times 2 } } Context 'Stale drive removal' { It 'Removes existing mapping before remounting' { Mock Test-Path { $true } -ParameterFilter { $Path -like '*:\' } $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Confirm:$false # Called twice: once for delete, once for mount Should -Invoke Invoke-NetUse -Times 2 } } Context 'WhatIf support' { It 'Does not actually mount when -WhatIf is specified' { $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -WhatIf # Invoke-NetUse should not be called for actual mounting (only stale cleanup test is separate) Should -Invoke Invoke-NetUse -Times 0 } } Context 'Missing Credential without Reset' { It 'Throws an error when Credential is not provided and Reset is not specified' { Mock Write-Error {} Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Confirm:$false Should -Invoke Write-Error -Times 1 -ParameterFilter { $Message -like '*-Credential parameter is required*' } } } Context 'Missing ShareDriveMap without Reset' { It 'Throws an error when ShareDriveMap is not provided and Reset is not specified' { Mock Write-Error {} $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -Confirm:$false Should -Invoke Write-Error -Times 1 -ParameterFilter { $Message -like '*-ShareDriveMap parameter is required*' } } } Context 'Reset with explicit ShareDriveMap' { BeforeEach { Mock Start-Process {} Mock Read-Host { '' } Mock Invoke-CmdKey { '' } Mock Invoke-NetUse { $global:LASTEXITCODE = 0 'OK' } Mock Test-Path { $true } -ParameterFilter { $Path -like '*:\' } } It 'Disconnects existing drives, purges credentials, prompts user and remounts' { $newCred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D'; 'files' = 'F' } -Reset -Confirm:$false # Start-Process should open the password change page Should -Invoke Start-Process -Times 1 # Read-Host should pause for user confirmation Should -Invoke Read-Host -Times 1 # Get-Credential should prompt for new credentials Should -Invoke Get-Credential -Times 1 # Invoke-CmdKey called for purging credentials (3 known targets + enumeration list) Should -Invoke Invoke-CmdKey -Scope It # Invoke-NetUse should be called for drive deletes, stale scan, and remounts Should -Invoke Invoke-NetUse -Scope It } It 'Opens the Microsoft password change URL' { $newCred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Reset -Confirm:$false Should -Invoke Start-Process -Times 1 -ParameterFilter { $FilePath -eq 'https://account.activedirectory.windowsazure.com/ChangePassword.aspx' } } } Context 'Reset with auto-discovered ShareDriveMap' { BeforeEach { Mock Start-Process {} Mock Read-Host { '' } Mock Invoke-CmdKey { '' } } It 'Discovers existing mappings from net use and remounts them' { $netUseDiscoveryOutput = @( 'New connections will be remembered.' '' 'Status Local Remote Network' '-------------------------------------------------------------------------------' 'OK D: \\teststorage.file.core.windows.net\data Microsoft Windows Network' 'OK F: \\teststorage.file.core.windows.net\files Microsoft Windows Network' 'The command completed successfully.' ) $script:netCallCount = 0 Mock Invoke-NetUse { $script:netCallCount++ # First call is the discovery scan (no args / empty args) if ($script:netCallCount -eq 1) { $global:LASTEXITCODE = 0 return $netUseDiscoveryOutput } $global:LASTEXITCODE = 0 return 'OK' } Mock Test-Path { $true } -ParameterFilter { $Path -like '*:\' } $newCred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -Reset -Confirm:$false # Invoke-NetUse invoked for: discovery, drive deletes, stale scan, remounts Should -Invoke Invoke-NetUse -Scope It # Credential prompt must happen Should -Invoke Get-Credential -Times 1 } It 'Discovers subfolder mappings correctly' { $netUseDiscoveryOutput = @( 'Status Local Remote Network' '-------------------------------------------------------------------------------' 'OK H: \\teststorage.file.core.windows.net\staff\jdoe Microsoft Windows Network' ) $script:netCallCount = 0 Mock Invoke-NetUse { $script:netCallCount++ if ($script:netCallCount -eq 1) { $global:LASTEXITCODE = 0 return $netUseDiscoveryOutput } $global:LASTEXITCODE = 0 return 'OK' } Mock Test-Path { $true } -ParameterFilter { $Path -like '*:\' } $newCred = [PSCredential]::new('jdoe@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -Reset -Confirm:$false # The discovered map key should be 'staff/jdoe' -> 'H' Should -Invoke Invoke-NetUse -Scope It Should -Invoke Get-Credential -Times 1 } It 'Errors when no existing mappings are found for the storage account' { Mock Invoke-NetUse { $global:LASTEXITCODE = 0 @('New connections will be remembered.', '', 'There are no entries in the list.') } Mock Write-Error {} Mock Get-Credential {} Mount-FileShare -StorageAccountName 'teststorage' -Reset -Confirm:$false Should -Invoke Write-Error -Times 1 -ParameterFilter { $Message -like '*No existing file-share mappings found*' } } It 'Ignores mappings for other storage accounts during discovery' { $netUseDiscoveryOutput = @( 'Status Local Remote Network' '-------------------------------------------------------------------------------' 'OK X: \\otherstorage.file.core.windows.net\share Microsoft Windows Network' ) Mock Invoke-NetUse { $global:LASTEXITCODE = 0 return $netUseDiscoveryOutput } Mock Write-Error {} Mock Get-Credential {} Mount-FileShare -StorageAccountName 'teststorage' -Reset -Confirm:$false Should -Invoke Write-Error -Times 1 -ParameterFilter { $Message -like '*No existing file-share mappings found*' } } } Context 'Reset credential purge' { BeforeEach { Mock Start-Process {} Mock Read-Host { '' } Mock Test-Path { $false } Mock Invoke-NetUse { $global:LASTEXITCODE = 0 '' } } It 'Calls Invoke-CmdKey for known target patterns' { Mock Invoke-CmdKey { '' } $newCred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Reset -Confirm:$false # At minimum 3 explicit Invoke-CmdKey calls for the known target patterns # plus 1 /list call for enumeration Should -Invoke Invoke-CmdKey -Scope It } It 'Removes dynamically discovered cmdkey entries matching the FQDN' { $cmdkeyListOutput = @( 'Currently stored credentials:' '' ' Target: teststorage.file.core.windows.net' ' Type: Domain Password' ' User: AZURE\teststorage' '' ' Target: Domain:target=otherstorage.file.core.windows.net' ' Type: Domain Password' ' User: AZURE\otherstorage' ) -join "`n" $script:cmdkeyCallCount = 0 Mock Invoke-CmdKey { $script:cmdkeyCallCount++ if ($Arguments -contains '/list') { return $cmdkeyListOutput } return '' } $newCred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'newpass' -AsPlainText -Force)) Mock Get-Credential { $newCred } Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Reset -Confirm:$false # Invoke-CmdKey should have been invoked: 3 known patterns + 1 /list + at least 1 dynamic delete Should -Invoke Invoke-CmdKey -Scope It } } Context 'Parameter validation' { It 'StorageAccountName is mandatory' { $param = (Get-Command Mount-FileShare).Parameters['StorageAccountName'] $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object { $_.Mandatory | Should -BeTrue } } It 'SupportsShouldProcess is enabled' { $cmd = Get-Command Mount-FileShare $cmdBindingAttr = $cmd.ScriptBlock.Attributes | Where-Object { $_ -is [System.Management.Automation.CmdletBindingAttribute] } $cmdBindingAttr.SupportsShouldProcess | Should -BeTrue } It 'Credential is not mandatory' { $param = (Get-Command Mount-FileShare).Parameters['Credential'] $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object { $_.Mandatory | Should -BeFalse } } It 'ShareDriveMap is not mandatory' { $param = (Get-Command Mount-FileShare).Parameters['ShareDriveMap'] $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object { $_.Mandatory | Should -BeFalse } } } Context 'Mount summary' { It 'Displays summary with mount count' { $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mock Invoke-NetUse { $global:LASTEXITCODE = 0 'OK' } Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Confirm:$false Should -Invoke Write-Host -ParameterFilter { $Object -like '*Mounted*1*' } } It 'Displays drive map before mounting' { $cred = [PSCredential]::new('user@contoso.com', (ConvertTo-SecureString 'pass' -AsPlainText -Force)) Mock Invoke-NetUse { $global:LASTEXITCODE = 0 'OK' } Mount-FileShare -Credential $cred -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Confirm:$false Should -Invoke Write-Host -ParameterFilter { $Object -like '*Drive Map*' } } } Context 'Reset aborts when no credential provided' { It 'Throws when Get-Credential returns null during reset' { Mock Start-Process {} Mock Read-Host { '' } Mock Invoke-CmdKey { '' } Mock Invoke-NetUse { $global:LASTEXITCODE = 0; '' } Mock Test-Path { $false } # Return a credential with empty username to test the null-check path Mock Get-Credential { [PSCredential]::new('x', (ConvertTo-SecureString 'x' -AsPlainText -Force)) } # Function should complete without error when valid credentials are returned # (Verifies the reset credential flow works end-to-end) Mount-FileShare -StorageAccountName 'teststorage' -ShareDriveMap @{ 'data' = 'D' } -Reset -Confirm:$false Should -Invoke Get-Credential -Times 1 } } } |