-
-
Notifications
You must be signed in to change notification settings - Fork 4
[codex] Add PowerShell PR checks for GraphEssentials #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| name: Test PowerShell | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - master | ||
| paths-ignore: | ||
| - '*.md' | ||
| - 'Docs/**' | ||
| - 'Examples/**' | ||
| - '.gitignore' | ||
| pull_request: | ||
| branches: | ||
| - master | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| test-windows-ps51: | ||
| name: Windows PowerShell 5.1 | ||
| runs-on: windows-latest | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Run tests | ||
| shell: powershell | ||
| run: ./GraphEssentials.Tests.ps1 | ||
|
|
||
| test-windows-ps7: | ||
| name: PowerShell 7 | ||
| runs-on: windows-latest | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Run tests | ||
| shell: pwsh | ||
| run: ./GraphEssentials.Tests.ps1 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| $PrimaryModule = Get-ChildItem -Path $PSScriptRoot -Filter '*.psd1' -File -ErrorAction Stop | ||
| if ($PrimaryModule.Count -ne 1) { | ||
| throw 'Expected exactly one PSD1 file in the repository root.' | ||
| } | ||
|
|
||
| $availablePester = Get-Module -ListAvailable -Name Pester | Where-Object { | ||
| $_.Version -ge [version] '5.0.0' | ||
| } | Select-Object -First 1 | ||
|
|
||
| if (-not $availablePester) { | ||
| Install-Module -Name Pester -Scope CurrentUser -Force -AllowClobber -SkipPublisherCheck -ErrorAction Stop | ||
| } | ||
|
|
||
| Import-Module Pester -Force -ErrorAction Stop | ||
|
|
||
| $configuration = [PesterConfiguration]::Default | ||
| $configuration.Run.Path = (Join-Path $PSScriptRoot 'Tests') | ||
| $configuration.Run.Exit = $true | ||
| $configuration.Should.ErrorAction = 'Continue' | ||
| $configuration.CodeCoverage.Enabled = $false | ||
| $configuration.Output.Verbosity = 'Detailed' | ||
|
|
||
| $result = Invoke-Pester -Configuration $configuration | ||
| if ($result.FailedCount -gt 0) { | ||
| throw "$($result.FailedCount) tests failed." | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| BeforeAll { | ||
| . (Join-Path $PSScriptRoot '..\Private\Get-GraphEssentialsErrorDetails.ps1') | ||
|
|
||
| function New-TestErrorRecord { | ||
| param( | ||
| [string] $Message | ||
| ) | ||
|
|
||
| try { | ||
| throw [System.Exception]::new($Message) | ||
| } catch { | ||
| return $_ | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Describe 'Get-GraphEssentialsErrorDetails' { | ||
| It 'detects nested permission scope errors from Graph SDK exception text' { | ||
| $errorRecord = New-TestErrorRecord -Message @' | ||
| Status: 403 (Forbidden) | ||
| ErrorCode: UnknownError | ||
| {"errorCode":"PermissionScopeNotGranted","message":"Authorization failed due to missing permission scopes"} | ||
| '@ | ||
|
|
||
| $result = $errorRecord | Get-GraphEssentialsErrorDetails -FunctionName 'Test-GraphEssentials' | ||
|
|
||
| $result.StatusCode | Should -Be 403 | ||
| $result.Code | Should -Be 'PermissionScopeNotGranted' | ||
| $result.IsPermissionDenied | Should -BeTrue | ||
| $result.Message | Should -Match 'missing permission scopes' | ||
| } | ||
|
|
||
| It 'detects missing resources' { | ||
| $errorRecord = New-TestErrorRecord -Message @' | ||
| Status: 404 (NotFound) | ||
| ErrorCode: Request_ResourceNotFound | ||
| Message: Resource could not be found | ||
| '@ | ||
|
|
||
| $result = $errorRecord | Get-GraphEssentialsErrorDetails -FunctionName 'Test-GraphEssentials' | ||
|
|
||
| $result.StatusCode | Should -Be 404 | ||
| $result.Code | Should -Be 'Request_ResourceNotFound' | ||
| $result.IsNotFound | Should -BeTrue | ||
| } | ||
|
|
||
| It 'detects transient transport failures' { | ||
| $errorRecord = New-TestErrorRecord -Message 'Received an unexpected EOF or 0 bytes from the transport stream.' | ||
|
|
||
| $result = $errorRecord | Get-GraphEssentialsErrorDetails -FunctionName 'Test-GraphEssentials' | ||
|
|
||
| $result.IsTransient | Should -BeTrue | ||
| $result.Message | Should -Match 'unexpected EOF' | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| BeforeAll { | ||
| . (Join-Path $PSScriptRoot '..\Private\Get-GraphEssentialsErrorDetails.ps1') | ||
| . (Join-Path $PSScriptRoot '..\Public\Get-MyRoleHistory.ps1') | ||
|
|
||
| foreach ($commandName in @( | ||
| 'Get-MgUser' | ||
| 'Get-MgGroup' | ||
| 'Get-MgServicePrincipal' | ||
| 'Get-MgRoleManagementDirectoryRoleDefinition' | ||
| 'Get-MgRoleManagementDirectoryRoleAssignmentScheduleRequest' | ||
| 'Get-MgRoleManagementDirectoryRoleEligibilityScheduleRequest' | ||
| )) { | ||
| if (-not (Get-Command -Name $commandName -ErrorAction SilentlyContinue)) { | ||
| Set-Item -Path ("Function:\" + $commandName) -Value { throw 'Test stub should be mocked.' } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Describe 'Get-MyRoleHistory' { | ||
| BeforeEach { | ||
| Mock -CommandName Get-MgUser -MockWith { @() } | ||
| Mock -CommandName Get-MgGroup -MockWith { @() } | ||
| Mock -CommandName Get-MgServicePrincipal -MockWith { @() } | ||
| Mock -CommandName Get-MgRoleManagementDirectoryRoleDefinition -MockWith { @() } | ||
| Mock -CommandName Get-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -MockWith { | ||
| throw [System.Exception]::new(@' | ||
| Status: 403 (Forbidden) | ||
| ErrorCode: UnknownError | ||
| {"errorCode":"PermissionScopeNotGranted","message":"Authorization failed due to missing permission scopes"} | ||
| '@) | ||
| } | ||
| Mock -CommandName Get-MgRoleManagementDirectoryRoleEligibilityScheduleRequest -MockWith { | ||
| throw [System.Exception]::new(@' | ||
| Status: 403 (Forbidden) | ||
| ErrorCode: UnknownError | ||
| {"errorCode":"PermissionScopeNotGranted","message":"Authorization failed due to missing permission scopes"} | ||
| '@) | ||
| } | ||
| } | ||
|
|
||
| It 'returns no history instead of terminating when PIM history permissions are missing' { | ||
| $warnings = $null | ||
| $result = Get-MyRoleHistory -DaysBack 7 -WarningVariable warnings -WarningAction SilentlyContinue | ||
|
|
||
| @($result).Count | Should -Be 0 | ||
| ($warnings -join ' ') | Should -Match 'Missing Microsoft Graph application permission' | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| BeforeAll { | ||
| . (Join-Path $PSScriptRoot '..\Private\Invoke-MyGraphBatchRequest.ps1') | ||
| . (Join-Path $PSScriptRoot '..\Private\Invoke-MyGraphBatchResponse.ps1') | ||
| } | ||
|
|
||
| Describe 'Invoke-MyGraphBatchResponse' { | ||
| It 'retries throttled batch subrequests and returns successful results' { | ||
| $initialResponse = [PSCustomObject]@{ | ||
| responses = @( | ||
| [PSCustomObject]@{ | ||
| id = 'summary_0_1' | ||
| status = 200 | ||
| body = [PSCustomObject]@{ value = @('ok') } | ||
| }, | ||
| [PSCustomObject]@{ | ||
| id = 'summary_0_2' | ||
| status = 429 | ||
| headers = [PSCustomObject]@{ 'Retry-After' = '0' } | ||
| body = [PSCustomObject]@{ | ||
| error = [PSCustomObject]@{ | ||
| code = 'UnknownError' | ||
| message = 'Too Many Requests' | ||
| } | ||
| } | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| $retryResponse = [PSCustomObject]@{ | ||
| responses = @( | ||
| [PSCustomObject]@{ | ||
| id = 'summary_0_2' | ||
| status = 200 | ||
| body = [PSCustomObject]@{ value = @('retried') } | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| $idMap = @{ | ||
| summary_0_1 = 'user1' | ||
| summary_0_2 = 'user2' | ||
| } | ||
| $requestsById = @{ | ||
| summary_0_1 = @{ | ||
| id = 'summary_0_1' | ||
| method = 'GET' | ||
| url = '/users/user1/authentication/methods' | ||
| } | ||
| summary_0_2 = @{ | ||
| id = 'summary_0_2' | ||
| method = 'GET' | ||
| url = '/users/user2/authentication/methods' | ||
| } | ||
| } | ||
|
|
||
| Mock -CommandName Start-Sleep -MockWith {} | ||
| Mock -CommandName Invoke-MyGraphBatchRequest -MockWith { return $retryResponse } | ||
|
|
||
| $results = Invoke-MyGraphBatchResponse -BatchResponses $initialResponse -IdMap $idMap -DataType 'Auth Methods Summary' -RequestsById $requestsById -WarningAction SilentlyContinue | ||
|
|
||
| Assert-MockCalled -CommandName Start-Sleep -Times 1 -Exactly | ||
| Assert-MockCalled -CommandName Invoke-MyGraphBatchRequest -Times 1 -Exactly | ||
| @($results).Count | Should -Be 2 | ||
| @($results | Where-Object Success).Count | Should -Be 2 | ||
| @($results | Where-Object { $_.Context -eq 'user2' -and $_.Success }).Count | Should -Be 1 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| Describe 'GraphEssentials module manifest' { | ||
| BeforeAll { | ||
| $testDirectory = Split-Path -Parent $PSCommandPath | ||
| $modulePath = Resolve-Path (Join-Path $testDirectory '..\GraphEssentials.psd1') | ||
| $moduleManifest = Import-PowerShellDataFile -Path $modulePath | ||
| } | ||
|
|
||
| It 'defines the expected root module' { | ||
| $moduleManifest.RootModule | Should -Be 'GraphEssentials.psm1' | ||
| } | ||
|
|
||
| It 'exports key public commands' { | ||
| $moduleManifest.FunctionsToExport | Should -Contain 'Get-MyRoleHistory' | ||
| $moduleManifest.FunctionsToExport | Should -Contain 'Get-MyUserAuthentication' | ||
| $moduleManifest.FunctionsToExport | Should -Contain 'Show-MyRole' | ||
| } | ||
|
|
||
| It 'declares Graph dependencies' { | ||
| $moduleManifest.RequiredModules.ModuleName | Should -Contain 'Microsoft.Graph.Authentication' | ||
| $moduleManifest.RequiredModules.ModuleName | Should -Contain 'Microsoft.Graph.Identity.Governance' | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This workflow is scoped to
master, but this repository’s active development branch ismain(the branch with recent merge commits), so PRs targetingmainwill not run these new checks. That means the intended PR gate is effectively disabled unless someone runsworkflow_dispatchmanually, which defeats the purpose of adding automated regression checks.Useful? React with 👍 / 👎.