tests/functions/Get-MtGitHubRateLimitMessage.Tests.ps1

BeforeAll {
    Import-Module "$PSScriptRoot/../../Maester.psd1" -Force
}

Describe 'Get-MtGitHubRateLimitMessage' {
    Context 'Non-rate-limit responses' {
        It 'Returns $null for status codes outside 403/429' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{ StatusCode = 500; Headers = @{} }
                $ex = [System.Exception]::new('Server error')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                Get-MtGitHubRateLimitMessage -ErrorRecord $err | Should -BeNullOrEmpty
            }
        }

        It 'Returns $null for 403 without rate-limit headers' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{ StatusCode = 403; Headers = @{} }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                Get-MtGitHubRateLimitMessage -ErrorRecord $err | Should -BeNullOrEmpty
            }
        }
    }

    Context 'Malformed headers' {
        It 'Returns $null when x-ratelimit-remaining is not a number and no retry-after header is set' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{
                    StatusCode = 403
                    Headers    = @{ 'x-ratelimit-remaining' = 'not-a-number' }
                }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                { Get-MtGitHubRateLimitMessage -ErrorRecord $err } | Should -Not -Throw
                Get-MtGitHubRateLimitMessage -ErrorRecord $err | Should -BeNullOrEmpty
            }
        }

        It 'Returns primary rate-limit message with "Resets at: unknown" when remaining is 0 and reset is malformed' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{
                    StatusCode = 403
                    Headers    = @{ 'x-ratelimit-remaining' = '0'; 'x-ratelimit-reset' = 'not-a-number' }
                }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $msg = Get-MtGitHubRateLimitMessage -ErrorRecord $err
                $msg | Should -Match 'rate limit'
                $msg | Should -Match 'Resets at: unknown'
            }
        }
    }

    Context 'Valid rate-limit headers' {
        It 'Returns primary rate-limit message with parsed reset time when remaining=0 and reset is valid' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{
                    StatusCode = 403
                    Headers    = @{ 'x-ratelimit-remaining' = '0'; 'x-ratelimit-reset' = '9999999999' }
                }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $msg = Get-MtGitHubRateLimitMessage -ErrorRecord $err
                $msg | Should -Match '^GitHub API rate limit encountered \(HTTP 403\)\. Resets at: '
                $msg | Should -Not -Match 'unknown'
            }
        }

        It 'Returns secondary rate-limit message when retry-after is present' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{
                    StatusCode = 429
                    Headers    = @{ 'retry-after' = '30' }
                }
                $ex = [System.Exception]::new('Too Many Requests')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $msg = Get-MtGitHubRateLimitMessage -ErrorRecord $err
                $msg | Should -Match 'secondary rate limit'
                $msg | Should -Match 'Retry after: 30s'
            }
        }
    }

    Context 'Body-fallback secondary rate-limit detection' {
        It 'Returns secondary rate-limit message for 403 when body mentions secondary rate limit and no retry-after header is present' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{ StatusCode = 403; Headers = @{} }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $err.ErrorDetails = [System.Management.Automation.ErrorDetails]::new('{"message":"You have exceeded a secondary rate limit. Please wait a few minutes before you try again."}')
                $msg = Get-MtGitHubRateLimitMessage -ErrorRecord $err
                $msg | Should -Match '^GitHub secondary rate limit encountered \(HTTP 403\)\.'
                $msg | Should -Match 'Retry after at least 60s'
            }
        }

        It 'Returns secondary rate-limit message for 429 when body mentions abuse detection and no retry-after header is present' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{ StatusCode = 429; Headers = @{} }
                $ex = [System.Exception]::new('Too Many Requests')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $err.ErrorDetails = [System.Management.Automation.ErrorDetails]::new('{"message":"Triggered abuse detection mechanism."}')
                $msg = Get-MtGitHubRateLimitMessage -ErrorRecord $err
                $msg | Should -Match '^GitHub secondary rate limit encountered \(HTTP 429\)\.'
                $msg | Should -Match 'Retry after at least 60s'
            }
        }

        It 'Still returns $null for 403 when body has unrelated message and no rate-limit headers' {
            InModuleScope Maester {
                $resp = [PSCustomObject]@{ StatusCode = 403; Headers = @{} }
                $ex = [System.Exception]::new('Forbidden')
                Add-Member -InputObject $ex -MemberType NoteProperty -Name Response -Value $resp
                $err = [System.Management.Automation.ErrorRecord]::new($ex, 'id', 'NotSpecified', $null)
                $err.ErrorDetails = [System.Management.Automation.ErrorDetails]::new('{"message":"Resource not accessible by personal access token"}')
                Get-MtGitHubRateLimitMessage -ErrorRecord $err | Should -BeNullOrEmpty
            }
        }
    }
}