Test/Get-AclItem.Tests.ps1
|
using namespace System using namespace System.IO using namespace System.Security.Cryptography using namespace System.Collections.Immutable Set-StrictMode -Version Latest BeforeAll { $moduleRoot = Split-Path -Path $PSScriptRoot -Parent Import-Module (Join-Path -Path $moduleRoot -ChildPath 'PSSecurity.psd1') -Force } AfterAll { Remove-Module PSSecurity -Force -ErrorAction SilentlyContinue } #### <h2 style="color: #DCA657;">Get-AclItem</h2> #### #### The fixture builds three files that each broke this function at some point: #### an ordinary file, a name containing wildcard characters, and a hidden item. #### Describe 'Get-AclItem' -Skip:(-not $IsWindows) { BeforeAll { $script:dir = Join-Path ([IO.Path]::GetTempPath()) ([guid]::NewGuid().ToString()) New-Item -ItemType Directory -Path $script:dir | Out-Null $script:file = Join-Path $script:dir 'a.txt' Set-Content -LiteralPath $script:file -Value 'a' -NoNewline # Square brackets are wildcard syntax. The function promises literal handling. $script:wildcardFile = Join-Path $script:dir 'b[1].txt' Set-Content -LiteralPath $script:wildcardFile -Value 'b' -NoNewline $script:hiddenFile = Join-Path $script:dir 'desktop.ini' Set-Content -LiteralPath $script:hiddenFile -Value 'x' -NoNewline (Get-Item -LiteralPath $script:hiddenFile -Force).Attributes = 'Hidden' $script:expectedFields = @( 'FullName', 'Name', 'Path', 'ItemType', 'Owner', 'Group', 'Access', 'CreationTime', 'LastAccessTime', 'LastWriteTime', 'Mode', 'Attributes', 'Security', 'AreAccessRulesCanonical', 'AreAuditRulesCanonical', 'AreAccessRulesProtected', 'AreAuditRulesProtected' ) } AfterAll { Remove-Item -LiteralPath $script:dir -Recurse -Force -ErrorAction SilentlyContinue } #### #### <b style="color: #D2A8FF;">Cases</b> #### #### - The function is exported, and is a function rather than an alias. It 'Is exported as a function' { $cmd = Get-Command -Module PSSecurity -Name 'Get-AclItem' -ErrorAction SilentlyContinue $cmd | Should -Not -BeNullOrEmpty $cmd.CommandType | Should -Be 'Function' } #### - A file returns every documented field. It 'Returns the documented field set for a file' { $item = Get-AclItem -LiteralPath $script:file $names = @($item.PSObject.Properties.Name) foreach ($field in $script:expectedFields) { $names | Should -Contain $field } } #### - A directory returns the same field set. One shape for both types is the #### whole point, and this is the case that catches a FileInfo-only property. It 'Returns the same field set for a directory' { # Regression: Length and IsReadOnly are FileInfo-only and threw on directories. $item = Get-AclItem -LiteralPath $script:dir $names = @($item.PSObject.Properties.Name) foreach ($field in $script:expectedFields) { $names | Should -Contain $field } } #### - `ItemType` distinguishes the two without the caller testing types. It 'Reports ItemType as the underlying type name' { (Get-AclItem -LiteralPath $script:file).ItemType | Should -Be 'FileInfo' (Get-AclItem -LiteralPath $script:dir).ItemType | Should -Be 'DirectoryInfo' } #### - Each nested access entry carries its documented fields. It 'Projects each ACE with the documented fields' { $access = @((Get-AclItem -LiteralPath $script:file).Access) $access.Count | Should -BeGreaterThan 0 $aceFields = @($access[0].PSObject.Properties.Name) foreach ($field in @('FileSystemRights', 'AccessControlType', 'IdentityReference', 'IsInherited', 'InheritanceFlags', 'PropagationFlags')) { $aceFields | Should -Contain $field } } #### - A missing path throws the function's own message, not a provider error. It 'Throws a named error when the path does not exist' { $missing = Join-Path $script:dir 'no-such-file.txt' { Get-AclItem -LiteralPath $missing } | Should -Throw '*can not find literal path*' } #### - Regression: `-Path` treats `b[1].txt` as a character class. A pattern #### matching two files returns two items and silently corrupts the result. It 'Treats wildcard characters in the path literally' { (Get-AclItem -LiteralPath $script:wildcardFile).Name | Should -Be 'b[1].txt' } #### - Binding from the pipeline resolves to the same absolute path. It 'Accepts the path from the pipeline' { ($script:file | Get-AclItem).FullName | Should -Be $script:file } #### - Regression: hidden items are the ones an ownership audit most wants. It 'Reads a hidden item' { # Get-ChildItem -Force is the normal way to feed this function, so hidden # items reach it in any real tree. Get-Item needs -Force to return them. (Get-AclItem -LiteralPath $script:hiddenFile).Name | Should -Be 'desktop.ini' } } #### #### --- #### |