tests/InvokeGraphRequest.Tests.ps1

#Requires -Version 7.0

BeforeAll {
    Import-Module "$PSScriptRoot/../IntuneBackup.psm1" -Force
    $script:token     = ConvertTo-SecureString -String 'test-token'   -AsPlainText -Force
    $script:tokenAlt  = ConvertTo-SecureString -String 'custom-token' -AsPlainText -Force
}

Describe 'Invoke-GraphRequest2' {
    BeforeEach {
        $script:savedToken = $env:GRAPH_TOKEN
    }
    AfterEach {
        $env:GRAPH_TOKEN = $script:savedToken
    }

    Context 'Basic request' {
        It 'makes a GET request to the Graph API' {
            Mock Invoke-RestMethod -ModuleName IntuneBackup {
                return [PSCustomObject]@{
                    value = @(
                        [PSCustomObject]@{ id = '1'; name = 'Item1' },
                        [PSCustomObject]@{ id = '2'; name = 'Item2' }
                    )
                }
            }

            $result = Invoke-GraphRequest2 -Uri '/v1.0/users' -Token $script:token
        }
    }

    Context 'Pagination handling' {
        It 'handles pagination by merging value arrays' {
            $script:pgCallCount = 0
            Mock Invoke-RestMethod -ModuleName IntuneBackup {
                $script:pgCallCount++
                if ($script:pgCallCount -eq 1) {
                    return [PSCustomObject]@{
                        value             = @(
                            [PSCustomObject]@{ id = '1'; name = 'Item1' },
                            [PSCustomObject]@{ id = '2'; name = 'Item2' }
                        )
                        '@odata.nextLink' = 'https://graph.microsoft.com/v1.0/users?$skip=2'
                    }
                }
                else {
                    return [PSCustomObject]@{
                        value = @(
                            [PSCustomObject]@{ id = '3'; name = 'Item3' },
                            [PSCustomObject]@{ id = '4'; name = 'Item4' }
                        )
                    }
                }
            }

            $result = Invoke-GraphRequest2 -Uri '/v1.0/users' -Token $script:token
            Should -Invoke Invoke-RestMethod -ModuleName IntuneBackup -Times 2 -Scope It
            $result.Count | Should -Be 4
        }
    }

    Context 'Raw object response' {
        It 'returns raw object when no value property' {
            Mock Invoke-RestMethod -ModuleName IntuneBackup {
                return [PSCustomObject]@{ id = '123'; name = 'SingleItem' }
            }

            $result = Invoke-GraphRequest2 -Uri '/v1.0/users/123' -Token $script:token
            $result.id | Should -Be '123'
            $result.name | Should -Be 'SingleItem'
        }
    }

    Context 'Retry logic' {
        It 'succeeds after transient network failure' {
            # with no status code, currently throws immediately (expected behavior)
            # this test verifies the function does NOT infinitely retry on unknown errors
            Mock Invoke-RestMethod -ModuleName IntuneBackup {
                throw [System.Net.WebException]'NetworkError'
            }

            { Invoke-GraphRequest2 -Uri '/v1.0/users' -Token $script:token } | Should -Throw
        }
    }

    Context 'Token handling' {
        It 'uses provided token in Authorization header' {
            Mock Invoke-RestMethod -ModuleName IntuneBackup { return [PSCustomObject]@{ value = @() } }

            Invoke-GraphRequest2 -Uri '/v1.0/users' -Token $script:tokenAlt
            Should -Invoke Invoke-RestMethod -ModuleName IntuneBackup -ParameterFilter {
                $Headers.Authorization -eq 'Bearer custom-token'
            } -Scope It
        }

        It 'throws when no token provided and env variable not set' {
            $env:GRAPH_TOKEN = $null
            { Invoke-GraphRequest2 -Uri '/v1.0/users' } | Should -Throw
        }
    }
}