Tests/GraphShell.Tests.ps1
|
#Requires -Modules Pester <# Pester tests for the GraphShell PowerShell module. Run locally with: Invoke-Pester -Path .\powershell\GraphShell\Tests\GraphShell.Tests.ps1 -Output Detailed #> BeforeAll { $script:ModuleRoot = Split-Path -Parent $PSScriptRoot $script:ManifestPath = Join-Path $ModuleRoot 'GraphShell.psd1' Remove-Module GraphShell -ErrorAction SilentlyContinue Import-Module $ManifestPath -Force -ErrorAction Stop } AfterAll { Remove-Module GraphShell -ErrorAction SilentlyContinue } Describe 'GraphShell module packaging' { It 'imports without error' { { Import-Module $script:ManifestPath -Force } | Should -Not -Throw } It 'has a valid module manifest' { { Test-ModuleManifest -Path $script:ManifestPath -ErrorAction Stop } | Should -Not -Throw } It 'exports exactly Get-GraphMapping, Get-GraphDetail, Get-GraphParity, Get-GraphLegacyMapping and Get-GraphPermission' { $exported = (Get-Command -Module GraphShell).Name | Sort-Object $exported | Should -Be @('Get-GraphDetail', 'Get-GraphLegacyMapping', 'Get-GraphMapping', 'Get-GraphParity', 'Get-GraphPermission') } It 'declares the GraphShell/Microsoft.Graph distinction in the manifest description' { $manifest = Test-ModuleManifest -Path $script:ManifestPath $manifest.Description | Should -Match 'does not replace the Microsoft Graph PowerShell SDK' } } Describe 'Get-GraphMapping -Cmdlet' { It 'resolves a known cmdlet to its REST relationship' { $result = Get-GraphMapping -Cmdlet 'Get-MgUser' $result | Should -Not -BeNullOrEmpty ($result | Where-Object Endpoint -eq '/users').Method | Should -Be 'GET' $result[0].PSObject.TypeNames | Should -Contain 'GraphShell.Mapping' } It 'resolves a renamed cmdlet to the current cmdlet' { $result = Get-GraphMapping -Cmdlet 'Remove-MgApplicationOwnerByRef' $result | Should -Not -BeNullOrEmpty $result[0].Cmdlet | Should -Be 'Remove-MgApplicationOwnerDirectoryObjectByRef' $result[0].RenamedFrom | Should -Be 'Remove-MgApplicationOwnerByRef' } It 'returns nothing for a cmdlet outside the Microsoft Graph SDK' { Get-GraphMapping -Cmdlet 'Get-AzRoleAssignment' | Should -BeNullOrEmpty } } Describe 'Get-GraphMapping -Endpoint' { It 'resolves a known REST endpoint to its cmdlet(s)' { $result = Get-GraphMapping -Endpoint '/users' $result | Should -Not -BeNullOrEmpty ($result.Cmdlet -contains 'Get-MgUser') | Should -Be $true } } Describe 'Get-GraphMapping -Permission' { It 'uses the permission reverse index to list related operations' { $result = Get-GraphMapping -Permission 'User.Read.All' $result | Should -Not -BeNullOrEmpty $result[0].MatchedPermission | Should -Be 'User.Read.All' ($result.Cmdlet -contains 'Get-MgUser') | Should -Be $true } It 'returns nothing for an unknown permission' { Get-GraphMapping -Permission 'Not.A.Real.Permission' | Should -BeNullOrEmpty } } Describe 'Get-GraphMapping -Module' { It 'returns operations for a known module name' { $result = Get-GraphMapping -Module 'Microsoft.Graph.Users' $result | Should -Not -BeNullOrEmpty ($result.ModuleName -contains 'Microsoft.Graph.Users') | Should -Be $true } } Describe 'Get-GraphMapping -Query' { It 'finds PIM-eligible role operations from a free-text concept' { $result = Get-GraphMapping -Query 'PIM eligible' $result | Should -Not -BeNullOrEmpty ($result | Where-Object Cmdlet -like '*RoleEligibilitySchedule*') | Should -Not -BeNullOrEmpty } It 'prioritizes records covering all concepts over unrelated compound-word matches' { $topResults = @(Get-GraphMapping -Query 'group membership' | Select-Object -First 10) $topResults.Cmdlet | Should -Contain 'Get-MgGroupMember' $topResults.ModuleName | Should -Not -Match 'DeviceManagement' } } Describe 'Get-GraphMapping output shape' { It 'returns real PowerShell objects usable with Select-Object/Where-Object' { $result = Get-GraphMapping -Permission 'User.Read.All' | Where-Object ApiVersion -eq 'v1.0' | Select-Object -First 1 Cmdlet, Module, Method, Endpoint $result | Should -Not -BeNullOrEmpty $result.PSObject.Properties.Name | Should -Contain 'Cmdlet' } } Describe 'Get-GraphDetail' { It 'loads only the necessary detail shard for a cmdlet' { $result = Get-GraphDetail -Name 'Get-MgUser' $result | Should -Not -BeNullOrEmpty ($result | Where-Object Endpoint -eq '/users').Permissions | Should -Not -BeNullOrEmpty $result[0].DetailShard | Should -Be 'g' } It 'skips generating a REST equivalent when the endpoint needs a path parameter' { $result = Get-GraphDetail -Name 'Get-MgUser' | Where-Object Endpoint -eq '/users/{user-id}' $result.RestEquivalent | Should -BeNullOrEmpty $result.RestNote | Should -Match 'path parameter' } It 'generates an Invoke-MgGraphRequest equivalent when the endpoint has no path parameters' { $result = Get-GraphDetail -Name 'Get-MgUser' | Where-Object Endpoint -eq '/users' $result.RestEquivalent | Should -Match 'Invoke-MgGraphRequest -Method GET' } It 'accepts pipeline input from Get-GraphMapping' { $result = Get-GraphMapping -Cmdlet 'Get-MgUser' | Select-Object -First 1 | Get-GraphDetail $result | Should -Not -BeNullOrEmpty } It 'warns and returns nothing for an unknown cmdlet' { $warnings = @() Get-GraphDetail -Name 'Get-MgDoesNotExist' -WarningVariable warnings -WarningAction SilentlyContinue | Should -BeNullOrEmpty $warnings.Count | Should -BeGreaterThan 0 } } Describe 'GraphShell catalog cache' { It 'reuses the cached index across calls instead of reloading the file' { InModuleScope GraphShell { $script:GraphCatalogCache = $null $first = Get-GraphCatalogIndex $second = Get-GraphCatalogIndex [object]::ReferenceEquals($first, $second) | Should -Be $true } } } Describe 'Get-GraphParity' { It 'reports SdkAndOpenApi for an endpoint that has both a cmdlet and an OpenAPI operation' { $result = Get-GraphParity -Endpoint '/users' -Method GET $result | Should -Not -BeNullOrEmpty $result.State | Should -Be 'SdkAndOpenApi' $result.HasCmdlet | Should -Be $true $result.Cmdlets | Should -Contain 'Get-MgUser' $result.PSObject.TypeNames | Should -Contain 'GraphShell.Parity' } It 'reports OpenApiOnly for an endpoint the Graph API exposes without a matching cmdlet' { $result = Get-GraphParity -Endpoint '/applications/{id}/appmanagementpolicies' -Method DELETE $result | Should -Not -BeNullOrEmpty $result.State | Should -Be 'OpenApiOnly' $result.HasCmdlet | Should -Be $false $result.EstimatedModule | Should -Be 'Applications' } It 'reports SdkOnly for a cmdlet-backed endpoint that has no matching OpenAPI operation' { $result = Get-GraphParity -Endpoint '/directory/impactedResources/addTag' -Method POST $result | Should -Not -BeNullOrEmpty $result.State | Should -Be 'SdkOnly' $result.HasCmdlet | Should -Be $true } It 'warns and returns nothing for an endpoint unknown to both the SDK and the Graph API' { $warnings = @() Get-GraphParity -Endpoint '/this/does/not/exist/anywhere' -WarningVariable warnings -WarningAction SilentlyContinue | Should -BeNullOrEmpty $warnings.Count | Should -BeGreaterThan 0 } It 'accepts pipeline input from Get-GraphMapping' { $result = Get-GraphMapping -Cmdlet 'Get-MgUser' | Get-GraphParity $result | Should -Not -BeNullOrEmpty $result | ForEach-Object { $_.State | Should -Be 'SdkAndOpenApi' } } It 'reports parity for every endpoint a cmdlet maps to when using -Cmdlet' { $result = Get-GraphParity -Cmdlet 'Get-MgUser' $result | Should -Not -BeNullOrEmpty $result.Count | Should -BeGreaterThan 1 $result | ForEach-Object { $_.HasCmdlet | Should -Be $true } $result | ForEach-Object { $_.PSObject.TypeNames | Should -Contain 'GraphShell.Parity' } } It 'binds -Cmdlet from a pipeline object exposing a Cmdlet property' { $result = [pscustomobject]@{ Cmdlet = 'Get-MgUser' } | Get-GraphParity $result | Should -Not -BeNullOrEmpty ($result.Cmdlets | Select-Object -Unique) | Should -Contain 'Get-MgUser' } It 'warns and returns nothing for a cmdlet outside the catalog when using -Cmdlet' { $warnings = @() Get-GraphParity -Cmdlet 'Get-ThisCmdletDoesNotExist' -WarningVariable warnings -WarningAction SilentlyContinue | Should -BeNullOrEmpty $warnings.Count | Should -BeGreaterThan 0 } } Describe 'Get-GraphLegacyMapping' { It 'resolves a legacy AzureAD command to its current Mg command' { $result = Get-GraphLegacyMapping -Command 'Get-AzureADUser' $result | Should -Not -BeNullOrEmpty $result | ForEach-Object { $_.GraphCommand | Should -Be 'Get-MgUser' } $result | ForEach-Object { $_.Status | Should -Be 'Mapped' } $result[0].PSObject.TypeNames | Should -Contain 'GraphShell.LegacyMapping' } It 'resolves a legacy MSOnline command to its current Mg command' { $result = Get-GraphLegacyMapping -Command 'Get-MsolUser' $result | Should -Not -BeNullOrEmpty $result | ForEach-Object { $_.GraphCommand | Should -Be 'Get-MgUser' } } It 'reports AmbiguousMapped when the official source lists more than one current command' { $result = Get-GraphLegacyMapping -Command 'Get-MsolDirSyncProvisioningError' $result | Should -Not -BeNullOrEmpty ($result | Select-Object -ExpandProperty GraphCommand -Unique).Count | Should -BeGreaterThan 1 $result | ForEach-Object { $_.Status | Should -Be 'AmbiguousMapped' } } It 'includes permissions only when -IncludeDetails is used' { $withoutDetails = Get-GraphLegacyMapping -Command 'Get-AzureADUser' | Select-Object -First 1 $withoutDetails.Permissions | Should -BeNullOrEmpty $withDetails = Get-GraphLegacyMapping -Command 'Get-AzureADUser' -IncludeDetails | Select-Object -First 1 $withDetails.Permissions | Should -Not -BeNullOrEmpty } It 'warns and returns nothing for a command outside the official legacy mapping' { $warnings = @() Get-GraphLegacyMapping -Command 'Get-DoesNotExist' -WarningVariable warnings -WarningAction SilentlyContinue | Should -BeNullOrEmpty $warnings.Count | Should -BeGreaterThan 0 } } Describe 'Get-GraphPermission' { It 'returns both the Application and Delegated definitions for a dual-type permission' { $result = Get-GraphPermission -Name 'User.Read.All' $result | Should -Not -BeNullOrEmpty ($result | Select-Object -ExpandProperty Type | Sort-Object -Unique) | Should -Be @('Application', 'Delegated') $result | ForEach-Object { $_.Id | Should -Not -BeNullOrEmpty } $result[0].PSObject.TypeNames | Should -Contain 'GraphShell.Permission' } It 'filters to a single type with -Type' { $result = Get-GraphPermission -Name 'User.Read.All' -Type Application $result | Should -Not -BeNullOrEmpty $result | ForEach-Object { $_.Type | Should -Be 'Application' } } It 'reports the related-operation count without duplicating the permission reverse index' { $result = Get-GraphPermission -Name 'User.Read.All' -Type Application $result.Operations | Should -BeGreaterThan 0 $result.Cmdlets | Should -BeNullOrEmpty } It 'includes cmdlets and endpoints only with -IncludeOperations' { $result = Get-GraphPermission -Name 'User.Read.All' -Type Application -IncludeOperations $result.Cmdlets | Should -Not -BeNullOrEmpty $result.Endpoints | Should -Not -BeNullOrEmpty } It 'warns and returns nothing for a permission outside the official catalog' { $warnings = @() Get-GraphPermission -Name 'Not.A.Real.Permission' -WarningVariable warnings -WarningAction SilentlyContinue | Should -BeNullOrEmpty $warnings.Count | Should -BeGreaterThan 0 } It 'accepts a permission name over the pipeline from Get-GraphMapping -Permission' { $permissionName = Get-GraphMapping -Permission 'User.Read.All' | Select-Object -First 1 -ExpandProperty MatchedPermission $result = $permissionName | Get-GraphPermission $result | Should -Not -BeNullOrEmpty } } |