Testing/Unit/PowerShell/Providers/PowerBIProvider/Export-PowerBIProvider.Tests.ps1

<#
 #
 # Due to how the Error handling was implemented, mocked API calls have to be mocked inside a
 # mocked CommandTracker class.
 #
 # The Power BI Admin REST API commonly fails with 403/401 when nobody has logged into
 # the Power BI portal, when the interactive user lacks the Fabric Administrator role,
 # or when the service principal security group tenant setting is not configured.
 # These tests lock down the custom guidance emitted for those conditions, and make sure
 # unrelated failures do NOT emit that guidance.
#>


$ProviderPath = "../../../../../Modules/Providers"
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "$($ProviderPath)/ExportPowerBIProvider.psm1") -Function Export-PowerBIProvider -Force
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "$($ProviderPath)/ProviderHelpers/CommandTracker.psm1") -Force

InModuleScope -ModuleName ExportPowerBIProvider {
    Describe -Tag 'PowerBIProvider' -Name "Export-PowerBIProvider" {
        BeforeAll {
            class MockCommandTracker {
                # The provider throws on unexpected failures, so its tracker instance is discarded before the
                # caller can inspect it. These static logs capture what was recorded anyway.
                static [System.Collections.Generic.List[string]]$Successful = [System.Collections.Generic.List[string]]::new()
                static [System.Collections.Generic.List[string]]$UnSuccessful = [System.Collections.Generic.List[string]]::new()

                static [void] Reset() {
                    [MockCommandTracker]::Successful.Clear()
                    [MockCommandTracker]::UnSuccessful.Clear()
                }

                [string[]]$SuccessfulCommands = @()
                [string[]]$UnSuccessfulCommands = @()

                [System.Object[]] TryCommand([string]$Command, [hashtable]$CommandArgs) {
                    throw "ERROR the Power BI provider should not route calls through TryCommand: $($Command)"
                }

                [System.Object[]] TryCommand([string]$Command) {
                    return $this.TryCommand($Command, @{})
                }

                [void] AddSuccessfulCommand([string]$Command) {
                    $this.SuccessfulCommands += $Command
                    [MockCommandTracker]::Successful.Add($Command)
                }

                [void] AddUnSuccessfulCommand([string]$Command) {
                    $this.UnSuccessfulCommands += $Command
                    [MockCommandTracker]::UnSuccessful.Add($Command)
                }

                [string[]] GetUnSuccessfulCommands() {
                    return $this.UnSuccessfulCommands
                }

                [string[]] GetSuccessfulCommands() {
                    return $this.SuccessfulCommands
                }
            }

            function Get-CommandTracker {}
            Mock -ModuleName ExportPowerBIProvider Get-CommandTracker {
                return [MockCommandTracker]::New()
            }

            # The provider emits a bare JSON fragment, so wrap it in braces before parsing.
            function ConvertFrom-ProviderJson {
                param (
                    [string]
                    $Json
                )
                ConvertFrom-Json "{$($Json.TrimEnd(','))}" -ErrorAction Stop
            }

            # Real tenant settings shape: an object carrying a tenantSettings array.
            function New-MockAdminSettings {
                [pscustomobject]@{
                    tenantSettings = @(
                        [pscustomobject]@{
                            settingName = "PublishToWeb"
                            title       = "Publish to web"
                            enabled     = $false
                        },
                        [pscustomobject]@{
                            settingName           = "ServicePrincipalAccessPermissionAPIs"
                            enabled               = $true
                            enabledSecurityGroups = @(
                                [pscustomobject]@{
                                    graphId = "00000000-0000-0000-0000-000000000000"
                                    name    = "PBI SP Group"
                                }
                            )
                        }
                    )
                }
            }

            # Mimics what Get-PowerBITenantSettingsRest (via Invoke-ScubaRestMethod) rethrows for an HTTP failure.
            function New-RestError {
                param ([string]$Message)
                [System.Net.WebException]::new($Message)
            }

            # Mimics an HTTP failure that carries a response, where the provider reads the status code
            # instead of the message text.
            class MockHttpException : System.Exception {
                [object]$Response

                MockHttpException([string]$Message, [int]$StatusCode) : base($Message) {
                    $this.Response = [pscustomobject]@{ StatusCode = $StatusCode }
                }
            }

            function New-RestErrorWithStatus {
                param ([string]$Message, [int]$StatusCode)
                [MockHttpException]::new($Message, $StatusCode)
            }
        }

        Context 'When the tenant has no Power BI license' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { throw "should not be called" }
            }

            It 'returns valid JSON without calling the Power BI API' {
                $Json = Export-PowerBIProvider -LicenseFound $false | Select-Object -Last 1
                { ConvertFrom-ProviderJson -Json $Json } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Get-PowerBITenantSettingsRest -Times 0 -Exactly
            }

            It 'reports powerbi_license_found as false with empty tenant settings' {
                $Json = Export-PowerBIProvider -LicenseFound $false | Select-Object -Last 1
                $Parsed = ConvertFrom-ProviderJson -Json $Json
                $Parsed.powerbi_license_found | Should -Be $false
                @($Parsed.powerbi_tenant_settings).Count | Should -Be 0
            }

            It 'does not require AccessToken or BaseUrl' {
                { Export-PowerBIProvider -LicenseFound $false } | Should -Not -Throw
            }

            It 'does not emit the permissions guidance banner' {
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
                Export-PowerBIProvider -LicenseFound $false | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }
        }

        # The Rego composes its report details from powerbi_license_reason, so the reason has to
        # survive the trip from Connect-Tenant through this provider and into the JSON verbatim.
        Context 'When reporting why the license check failed' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { throw "should not be called" }
            }

            It 'emits the tenant-level reason' {
                $Reason = 'No Power BI or Fabric license found in the tenant.'
                $Json = Export-PowerBIProvider -LicenseFound $false -LicenseReason $Reason | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_reason | Should -Be $Reason
            }

            It 'emits the per-user reason' {
                $Reason = 'Current user does not have a Power BI or Fabric license assigned. Assign a license (e.g., Microsoft Fabric (Free), Power BI Pro) to the running user.'
                $Json = Export-PowerBIProvider -LicenseFound $false -LicenseReason $Reason | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_reason | Should -Be $Reason
            }

            # Parentheses and commas in the per-user reason must not break the JSON fragment.
            It 'produces valid JSON for a reason containing punctuation' {
                $Reason = 'Assign a license (e.g., Microsoft Fabric (Free), Power BI Pro) to the "running" user.'
                $Json = Export-PowerBIProvider -LicenseFound $false -LicenseReason $Reason | Select-Object -Last 1
                { ConvertFrom-ProviderJson -Json $Json } | Should -Not -Throw
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_reason | Should -Be $Reason
            }

            # Empty is what makes the Rego fall back to its original generic message.
            It 'emits an empty reason when none is supplied' {
                $Json = Export-PowerBIProvider -LicenseFound $false | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_reason | Should -Be ''
            }

            It 'emits an empty reason on the licensed path' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { New-MockAdminSettings }
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_reason | Should -Be ''
                (ConvertFrom-ProviderJson -Json $Json).powerbi_access_denied_reason | Should -Be ''
            }
        }

        Context 'When LicenseFound is true but credentials are incomplete' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { throw "should not be called" }
            }

            It 'throws when AccessToken is missing' {
                { Export-PowerBIProvider -LicenseFound $true -BaseUrl 'https://api.powerbi.com' } |
                    Should -Throw '*AccessToken and BaseUrl must be provided*'
            }

            It 'throws when BaseUrl is missing' {
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' } |
                    Should -Throw '*AccessToken and BaseUrl must be provided*'
            }

            It 'throws when AccessToken is an empty string' {
                { Export-PowerBIProvider -LicenseFound $true -AccessToken '' -BaseUrl 'https://api.powerbi.com' } |
                    Should -Throw '*AccessToken and BaseUrl must be provided*'
            }

            It 'throws when BaseUrl is an empty string' {
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl '' } |
                    Should -Throw '*AccessToken and BaseUrl must be provided*'
            }

            It 'does not call the Power BI API when credentials are incomplete' {
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' } | Should -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Get-PowerBITenantSettingsRest -Times 0 -Exactly
            }
        }

        Context 'When the Power BI API returns tenant settings' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { New-MockAdminSettings }
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
            }

            It 'returns valid JSON' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                { ConvertFrom-ProviderJson -Json $Json } | Should -Not -Throw
            }

            It 'projects tenantSettings into powerbi_tenant_settings keyed by settingName' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Settings = @((ConvertFrom-ProviderJson -Json $Json).powerbi_tenant_settings)
                $Settings.Count | Should -Be 2
                $Settings.settingName | Should -Contain 'PublishToWeb'
                $Settings.settingName | Should -Contain 'ServicePrincipalAccessPermissionAPIs'
            }

            It 'preserves nested security group details that the Rego needs' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Settings = @((ConvertFrom-ProviderJson -Json $Json).powerbi_tenant_settings)
                $SpSetting = $Settings | Where-Object { $_.settingName -eq 'ServicePrincipalAccessPermissionAPIs' }
                @($SpSetting.enabledSecurityGroups).Count | Should -Be 1
                $SpSetting.enabledSecurityGroups[0].name | Should -Be 'PBI SP Group'
            }

            It 'records Get-PowerBITenantSettingsRest as a successful command so the Rego resolves it' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Parsed = ConvertFrom-ProviderJson -Json $Json
                @($Parsed.powerbi_successful_commands) | Should -Contain 'Get-PowerBITenantSettingsRest'
                @($Parsed.powerbi_unsuccessful_commands).Count | Should -Be 0
            }

            It 'reports powerbi_license_found as true' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_license_found | Should -Be $true
            }

            It 'calls Get-PowerBITenantSettingsRest with the supplied token and base URL' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.high.powerbigov.us' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Get-PowerBITenantSettingsRest -Times 1 -Exactly -ParameterFilter {
                    $BaseUrl -eq 'https://api.high.powerbigov.us' -and
                    $AccessToken -eq 'mock-token'
                }
            }

            It 'does not emit the permissions guidance banner on success' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }
        }

        Context 'When the Power BI API returns an empty response' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
            }

            It 'throws an actionable error when an empty array is returned' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { @() }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' } |
                    Should -Throw '*No tenant settings were returned*'
            }

            It 'throws an actionable error when null is returned' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { $null }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' } |
                    Should -Throw '*No tenant settings were returned*'
            }

            It 'does not emit the permissions guidance banner for an empty response' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { @() }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' } | Should -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            It 'still returns valid JSON when the response lacks a tenantSettings property' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { [pscustomobject]@{ unexpected = 'shape' } }
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                { ConvertFrom-ProviderJson -Json $Json } | Should -Not -Throw
            }
        }

        Context 'When the Power BI API returns 403 Forbidden under interactive auth' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
            }

            It 'returns the interactive fix in powerbi_access_denied_reason' {
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Result = ConvertFrom-ProviderJson -Json $Json
                $Result.powerbi_access_denied_reason | Should -Match '403 Forbidden'
                $Result.powerbi_access_denied_reason | Should -Match 'Fabric Administrator role'
                @($Result.powerbi_tenant_settings).Count | Should -Be 0
                $Result.powerbi_successful_commands | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
            }

            It 'tells the user they need the Fabric Administrator role' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'Fabric Administrator role'
                }
            }

            It 'tells the user to sign into the Power BI portal at least once' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'sign into the Power BI portal at least once'
                }
            }

            It 'does not show the service principal security group guidance' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly -ParameterFilter {
                    $MessageData -match 'security group for the service principal'
                }
            }
        }

        Context 'When the Power BI API returns 403 Forbidden under service principal auth' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
            }

            It 'returns the service principal fix in powerbi_access_denied_reason' {
                $Json = Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Result = ConvertFrom-ProviderJson -Json $Json
                $Result.powerbi_access_denied_reason | Should -Match 'security group for the service principal'
                $Result.powerbi_access_denied_reason | Should -Match 'noninteractive\.md#power-bi-tenant-setting'
                $Result.powerbi_access_denied_reason | Should -Match 'Tenant.Read.All'
            }

            It 'tells the user to configure a security group for the service principal' {
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'security group for the service principal'
                }
            }

            It 'links to the noninteractive Power BI tenant setting documentation' {
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'noninteractive\.md#power-bi-tenant-setting'
                }
            }

            It 'does not show the interactive Fabric Administrator guidance' {
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly -ParameterFilter {
                    $MessageData -match 'Fabric Administrator role'
                }
            }

            It 'treats -CertificateBasedAuth:$false as interactive auth' {
                Export-PowerBIProvider -CertificateBasedAuth:$false -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'Fabric Administrator role'
                }
            }
        }

        Context 'When the Power BI API returns 401 Unauthorized' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (401) Unauthorized.")
                }
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
            }


            It 'reports 401 Unauthorized in powerbi_access_denied_reason' {
                $Json = Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_access_denied_reason | Should -Match '401 Unauthorized'
            }

            It 'shows the service principal guidance under service principal auth' {
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'security group for the service principal'
                }
            }

            It 'shows the interactive guidance under interactive auth' {
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'Fabric Administrator role'
                }
            }
        }

        # When there's a response, its status code decides rather than the message text
        Context 'When the failure carries an HTTP response' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
                Mock -ModuleName ExportPowerBIProvider Write-Warning {}
            }

            # PS 7 formats this as "403 (Forbidden)", so the (403) message fallback would not match it
            It 'shows the fix for a 403 using the status code' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestErrorWithStatus -Message "Response status code does not indicate success: 403 (Forbidden)." -StatusCode 403)
                }
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_access_denied_reason | Should -Match '403 Forbidden.*Fabric Administrator role'
            }

            It 'does not show guidance for a 500 whose message contains (403)' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestErrorWithStatus -Message "Upstream call failed with (403) while proxying" -StatusCode 500)
                }
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                (ConvertFrom-ProviderJson -Json $Json).powerbi_access_denied_reason | Should -Be ''
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }
        }

        Context 'When the Power BI API fails for a reason unrelated to permissions' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
                Mock -ModuleName ExportPowerBIProvider Write-Warning {}
            }

            # Matches what TryCommand did before, so the report shows these policies as errors
            # instead of leaving Power BI out
            It 'still returns JSON with Get-PowerBITenantSettingsRest unsuccessful and no access denied reason' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (429) Too Many Requests.")
                }
                $Json = Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Select-Object -Last 1
                $Result = ConvertFrom-ProviderJson -Json $Json
                $Result.powerbi_unsuccessful_commands | Should -Contain 'Get-PowerBITenantSettingsRest'
                $Result.powerbi_successful_commands | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
                $Result.powerbi_access_denied_reason | Should -Be ''
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Warning -Times 1 -Exactly
            }

            # Guards against the guidance banner firing on any failure, which would send
            # users chasing permissions problems they do not have.
            It 'does not emit guidance for a 500 Internal Server Error' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (500) Internal Server Error.")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            It 'does not emit guidance for a 429 Too Many Requests' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (429) Too Many Requests.")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            It 'does not emit guidance for a 404 Not Found' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (404) Not Found.")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            It 'does not emit guidance for a network failure with no status code' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "Unable to connect to the remote server")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            # When the exception has no HTTP response, the provider falls back to looking for "(401)" or
            # "(403)" in the error message. These messages mention 403 or Forbidden without that exact
            # form, so they must not be mistaken for a permissions error.
            It 'does not emit guidance when a message contains 403 outside of parentheses' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "Correlation id 4030f1ac-0000-0000-0000-000000000000 request failed")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }

            It 'does not emit guidance when a message contains Forbidden but no status code' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "Operation Forbidden by tenant policy")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -Times 0 -Exactly
            }
        }

        Context 'Debug logging of the permissions guidance' {
            BeforeEach {
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
                Mock -ModuleName ExportPowerBIProvider Write-Warning {}
                Mock -ModuleName ExportPowerBIProvider Write-ScubaLog {}
            }

            It 'logs the interactive guidance on a 403' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 1 -Exactly -ParameterFilter {
                    $Data.AuthType -eq 'Interactive' -and
                    $Data.Guidance -match 'Fabric Administrator role' -and
                    $Data.Error -match '403'
                }
            }

            It 'logs the service principal guidance on a 403' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 1 -Exactly -ParameterFilter {
                    $Data.AuthType -eq 'ServicePrincipal' -and
                    $Data.Guidance -match 'security group for the service principal' -and
                    $Data.Guidance -match 'noninteractive\.md#power-bi-tenant-setting'
                }
            }

            It 'logs the guidance on a 401' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (401) Unauthorized.")
                }
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 1 -Exactly
            }

            It 'logs the guidance at Info level' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 1 -Exactly -ParameterFilter {
                    $Level -eq 'Info'
                }
            }

            It 'does not log guidance for a failure unrelated to permissions' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (500) Internal Server Error.")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 0 -Exactly -ParameterFilter {
                    $Message -eq 'Power BI permissions guidance'
                }
            }

            It 'does not log guidance when the API call succeeds' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { New-MockAdminSettings }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -Times 0 -Exactly
            }

            # Guards the console and log copies against drifting apart.
            It 'logs the same guidance text it prints to the console' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-Information -ParameterFilter {
                    $MessageData -match 'Fabric Administrator role'
                }
                Should -Invoke -ModuleName ExportPowerBIProvider -CommandName Write-ScubaLog -ParameterFilter {
                    $Data.Guidance -match 'Fabric Administrator role'
                }
            }
        }

        Context 'Command tracking' {
            BeforeEach {
                [MockCommandTracker]::Reset()
                Mock -ModuleName ExportPowerBIProvider Write-Information {}
                Mock -ModuleName ExportPowerBIProvider Write-Warning {}
            }

            It 'records Get-PowerBITenantSettingsRest as unsuccessful when the API returns 403 Forbidden' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                [MockCommandTracker]::UnSuccessful | Should -Contain 'Get-PowerBITenantSettingsRest'
                [MockCommandTracker]::Successful | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
            }

            It 'records Get-PowerBITenantSettingsRest as unsuccessful when the API returns 401 Unauthorized' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (401) Unauthorized.")
                }
                Export-PowerBIProvider -CertificateBasedAuth -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                [MockCommandTracker]::UnSuccessful | Should -Contain 'Get-PowerBITenantSettingsRest'
                [MockCommandTracker]::Successful | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
            }

            It 'records Get-PowerBITenantSettingsRest as unsuccessful when the API fails for an unrelated reason' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (500) Internal Server Error.")
                }
                { Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null } | Should -Not -Throw
                [MockCommandTracker]::UnSuccessful | Should -Contain 'Get-PowerBITenantSettingsRest'
                [MockCommandTracker]::Successful | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
            }

            It 'records Get-PowerBITenantSettingsRest only as successful when the API call succeeds' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest { New-MockAdminSettings }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                [MockCommandTracker]::Successful | Should -Contain 'Get-PowerBITenantSettingsRest'
                [MockCommandTracker]::UnSuccessful | Should -Not -Contain 'Get-PowerBITenantSettingsRest'
            }

            It 'does not double count Get-PowerBITenantSettingsRest on the failure path' {
                Mock -ModuleName ExportPowerBIProvider Get-PowerBITenantSettingsRest {
                    throw (New-RestError -Message "The remote server returned an error: (403) Forbidden.")
                }
                Export-PowerBIProvider -LicenseFound $true -AccessToken 'mock-token' -BaseUrl 'https://api.powerbi.com' | Out-Null
                @([MockCommandTracker]::UnSuccessful).Count | Should -Be 1
            }
        }
    }
}

AfterAll {
    Remove-Module ExportPowerBIProvider -Force -ErrorAction SilentlyContinue
    Remove-Module CommandTracker -Force -ErrorAction SilentlyContinue
}

# SIG # Begin signature block
# MIIu9wYJKoZIhvcNAQcCoIIu6DCCLuQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCl/2VE/xV9YfQf
# AYlfAlt8tDA+AusqiBZ9+lGR/l0OPKCCE6MwggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggdXMIIFP6ADAgECAhAMM6tnPejLgA9WVhXroQvSMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjYwMTE0MDAwMDAwWhcNMjcwMTEzMjM1OTU5WjBfMQsw
# CQYDVQQGEwJVUzEdMBsGA1UECBMURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV
# BAcTCldhc2hpbmd0b24xDTALBgNVBAoTBENJU0ExDTALBgNVBAMTBENJU0EwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCuXYolNHqlh6smLTE592waXheZ
# 8VHzxeds4pMaepGuwmjf8d1jG9wUNuJX9/qb0a1dgGz5D/EAz5NRTIin4SZYQEE8
# qvdl2yQJ5uWxXIjsFbrOyc1fWscUXw0Kt7OPLOafcEkdDoe8K0tO4h2GL3RWRzjp
# uLfQhhnAmD6NT1l+ughnfmarV/ODgIn/RFR4YORlu4YP2xQX6KRxeTDslg7F+z6X
# +t87/U8m8gQ9XTm5kBmteP4GcE/ytnyI+ScIxNRybzGomWIBm848XDE5yYhlYQ2R
# SnCoo6M4CRqp9WFGVyoLkoPP0OlxzryKWaE1/nuPbYG/kf/rUB1OhqxvSSGwmNhs
# vkkjsC0Z9H5Jy6heFdoxOu/+ZQksKoP/fMvHxuCCtkIJbV8tk0oT6MQ8EJbgsWDZ
# TKhui1wxW6JIZyBOMPWoZUOouOzo2h5Cz7LBPKME5FkcUzcs47lpRlDkJco4PLcj
# wJSo4XPnx3G/2DIjNEFNyfKCWfH8uW6nJjmDBiveFZ2j0YvgdQ+7MOjQnw7R/MAD
# DTagrKl3rLV60+X2TY6/onKhCUuU3pMAjVbOwZ3PkzDLZnsEGRfm6hgp6014aXml
# t8h4nu+uC41U8vUSGHl0vqKuzvmShLmnI+Iv0l95pmnqomuCZzRDrjEoaLPx7OxL
# Dy/Id9E7yQDip4jBdQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8R
# hvv+YXsIiGX0TkIwHQYDVR0OBBYEFN30sVU+fpfQQfPQWMhkO1qd+MxoMD4GA1Ud
# IAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
# gbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEF
# BQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t
# MFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJ
# BgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQA77T42YiOx5wWPItgo+kvB+Gzb
# ZFCRHRFfTAZZNQt9o/0CqNmyA2xFklX4t5Z9VNdiIOx14AnmJdQkcRdk3vsU5gby
# jKEup7LTWtvcWrl6hQwGNt3l892BgUbPKsPBE+AriktVqn5yMSXVVzeboqsqAG8e
# Syei0B54/QdgR4whfHvQ/qpCACsJTlJgAykXVgDPJNKnQ7wc17loLQutqF1JUcbO
# XKWt1AA9Zas5q30LDZzZeK4B56yojK68CTQXN7toSRFIuZDMKKDfIZpCX8cmbaaO
# DFOOu44/QWMv+Xc6+ISYGkrTTzqWhOqiXgLVBeXGn/WrOJJ8R29mZMneCpBesCLs
# YII1gCFOo7Vt6mvOKxAPQ3KhJYBFEHkp+GI65koaQkO2xv50iLS0+/j2YC66uviU
# MFe0JEOdXuE7Rn/OmWNSzQ+6kPNYDJQASQ974C3wUejJoMtGZEzoTbly/HufQTrd
# rhcL2aC69CxSN+idTXPLC9UT3xo4sFdOw+hXkmbXtoB1GDsd5p1TWFgRbnTXDkbM
# YMBWYVB6/Tk1bzwj4iTp4g0YrtB628FXPX/ko+JWZlv0Ea865S23w1uGlnDNVxIi
# +8oi74G5DM66Q6ENt6+3WoRGRrdoyE6uCh1haY+oPYSgumb0ozzrp8tw89TRKVrK
# KSXGBxlExEvjQ6dYwjGCGqowghqmAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0
# IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDDOrZz3oy4AP
# VlYV66EL0jANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh
# AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM
# BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCRUnkyQswsKljrxbe9hbexyUzD
# BeF9f1nJxHbInDVtIDANBgkqhkiG9w0BAQEFAASCAgAiG+6wBPPMkNzCfhTr5Ssx
# +UXcAnmcgUiIlbedf8zvDzvx0Ogan/sJwxG0TydtBDDb68Luo+tENnNs1eEu+1BZ
# QzArN1eiUYwYYbkrlFV7qKmZO/POPlep74emp0kzKwy9Di//OInJIgRnolK6pOD4
# w4krzWlKa8yP6V4qwv8d1x+iHDzxJrg6vKXFCiMzss1/N8VlwJjyV60aj9NQRKFu
# lR/gYUWv0tPvCErN8pQbyVoM4IOEKi4oGgY4ZPekdxTMYLpa+O98em1DidLiLoVx
# jn0MDbdAbdkWtU3IVnp3p5ZRvG72WuOwo8YrKYQfayuDvoTb32L1QsBHhiDiDmB+
# UY32iUZ6tKGH7lu9ZOp21bFmyTdf+lhQ70nbZZxiBZh8/VWN9DWAvY63oyLYv2jq
# 1EG4lghvfy6HQ6afXHffedsPzSMawuy3BzBEq4AGprkw8mKxrhZRLl7kQfz16+5M
# vchM3t4slPpvvASri17WiOry7A7M4GnRoKxmJ8ITg9zjBGhEAhk9JfEG8rypKFgO
# 8dK7utMmh5ZqNF9oQu+9n/jLgnf/svltxE8ab1m0lKBsW3XiFLUyjNL55cDGoUYF
# ALrego/hi6jOS2/sGSxyt3cDzdGNJX++PyMBRn0eCrbTqLkgXIiBn3Ts+q09++Uw
# gcmvFFzy487a4Cdnu9+TD6GCF3cwghdzBgorBgEEAYI3AwMBMYIXYzCCF18GCSqG
# SIb3DQEHAqCCF1AwghdMAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQ
# AQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCAKotoCz2/f
# I5vrF7aC1QxLptSGRjGanLR6UpJRe0s5BQIRAM70N2O5ATej9n5GJ5O3uWYYDzIw
# MjYxMDA4MTkxNDI2WqCCEzowggbtMIIE1aADAgECAhAIT9wzT35FTtvDD4/5khg1
# MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy
# dCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBp
# bmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwHhcNMjYwODA1MDAwMDAwWhcNMzcx
# MTA0MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu
# Yy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBS
# ZXNwb25kZXIgMjAyNiAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# tnum8sn+zUr41JtMZbP9OMYw+HwJDpG5xkIu/lqcfNYmMX81YmsUiHLbh9ykpeWB
# GKTLhYBrAN9Tdg/QEzG32XcObmgIblnr0CoQ3WSAeDZ6nH6X6VkFyYkJw3QBJREw
# vm4UhLzSxmwPA7cFKRTEOMsmEEj6qJk/dqLEAL+oQYuOwE2UuiX1Vnul8YReIyWd
# 4kgLn9gq6LNXM0UplkR6jL/QHxmb6fMoGBJYbnaUI7XD6cKDpekK2SVMld4iDbze
# HDtOaaxldH5IxuNusQ69nd8/ZXEiB5Hbxj3RlK13cX1W4DlFXKdv/CEhM8Cj1vvl
# mvhNroyPdRGbbpBlgyf8Wdu5N6ByhFwURn0U6ozlPoxN22v+fviUhP+6DR547OZn
# pBMWDfei1f5sVGwiiW/KQTWOK97g+4RJpPzPNV4VYMAwO2jM2Aty2QYPVmOQTJm0
# msuXnJrSbl2gf9JylpkJlWXqk1Q4LJsxz+TELoQCZIljbgvTJgoPU2R12ydv8i1U
# qL/adelA0y7U9Pmmtbze9Xx3rtajC5SzQd1jgfwAwsa90v9YcSPdmeoyoBBA/27c
# CL237l5DTYYPDLQ4ON3OLTGWnvRb6jDrf/T75gMRfUzSLCBQfBusm9+mSWRlC/Df
# 6S/e9Q8i13CuhzOT2Jx+V/nlbXM4QoBwlUAhelwwJT0CAwEAAaOCAZUwggGRMAwG
# A1UdEwEB/wQCMAAwHQYDVR0OBBYEFBTJY4owLtRK+26U8+bjQH717M3iMB8GA1Ud
# IwQYMBaAFO9vU0rp5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNV
# HSUBAf8EDDAKBggrBgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1w
# aW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFt
# cGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEE
# AjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAI3FOmEenVIK35msCYB+
# fShAsWvSYvLBItoNdAgQ2jIqrGsVsluXMJU/+mRebBc52s6lbKAvOVPXaizmKkML
# LflEEKDZQx4CkS2t8aHPjkXha3hYZ010htFa3dhNgmalH5vuWvh3tTCf4frTS7gP
# tGc4Z/xaPhQ2AB1mR8eEe/WbH0RWHvVIl6VwQ3+g5FKNfN2N/DWJkf13w2H+2Gfq
# Efbd35Ww8CvoYBjLNIDTadcPWdgsjsiOaK/7EsKJgLjUNIVgvcaFOLLQ/GlrA+0Z
# HJoFUbOr5SJN8zykPspXIXlpDJY/gqFUZRROeab9GVgmhbdOJcD/63RhxPahFUGb
# ckRONqMe6DYAv6/mOG0pWd3cPStsdcS7buj5DyniwRY8yooMH6ptx5vpP/pZzBPB
# eZD2U4IsthyxB5Jaa8qrOkB5z160TXiM5ADMspZ0TfD9MJoq0tFpFPssKRFhWeED
# YPvcUuN7U7lvcdHl4ezQ3NT/7Ffs1sR1yh/LRbdZ3B3Vc6q2WmD8mDC0p9kzl2o7
# 3iVtS946IkEj7FkRsZGww1teYxERROC745xrtjvcw9ZyyUjHZWGRIpJeMNsPquCD
# f0fkyHtB+J4AiNZqCQk23rxh+KbpyMTNVKItJ5l92Svl20U9NbqMBOVYl1h54NEY
# LJq1/xHWFKPNK903zJZA9P2DMIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJ
# hjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1
# OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/
# BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYg
# U0hBMjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# tHgx0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkb
# sDpz4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+
# AbdJgMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAkt
# VJMQbzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3
# Gmq6bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv8
# 2wnJRfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638Z
# xqU14lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7V
# vwDDjAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2N
# L+cnT6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTK
# IsDq1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUoz
# ZtqgPrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIG
# A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtO
# MB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIB
# hjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQw
# QwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N
# 0IfFiBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7J
# d2E4/iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1j
# EMK/DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdog
# P8HRtrYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMv
# NYY2o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBld
# NE2K9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjT
# Y6hc3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/B
# dHLiRu7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyI
# PbAvjSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8
# dC3JE3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2j
# NrbM1pD2T7m3XDCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI
# hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
# MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz
# dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290
# IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww
# IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5
# 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH
# hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6
# Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ
# ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b
# A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9
# WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU
# tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo
# ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J
# vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP
# orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB
# Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr
# oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt
# MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF
# BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw
# BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH
# vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8
# UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn
# f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU
# jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j
# LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3ww
# ggN4AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0
# MDk2IFNIQTI1NiAyMDI1IENBMQIQCE/cM09+RU7bww+P+ZIYNTANBglghkgBZQME
# AgEFAKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkF
# MQ8XDTI2MTAwODE5MTQyNlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUUdmr2gNJ
# c9hPQmaspIJI5rNpxDkwLwYJKoZIhvcNAQkEMSIEIBS8FyD1d6/D9VFD21nku5A9
# GPRE39kvvV5PpV7+MBNtMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIC2gnaf0Ex+f
# 5y22xebpyWVnVa8EPx6nQswNISDhQev8MA0GCSqGSIb3DQEBAQUABIICAD36oUX0
# hikm0/YLoVtkMenNe16/WGBV50sjQH2XAs5diGnKahRWYQgE37jshNjfaoPAOuj9
# Sf+KK+6hyw1pkqgO5xN/jIFTOy9GtAXPPL5OU1tvSaQaXOUEy3B/JD6/HNdqTSj7
# nfs8ffoE54/PAnfF5RmNk+ZXwz53JRJ8NUmDii/BXw2dJCqAMCy4R4egNXJhY14c
# HqrB3J/ggAvMXgrv5+nWIiJx/i58DGyUrDR4tWWn9FvQzHh51Z9BJUMTpqrR3Thi
# D7eUgShppdxBCTW5MuUvPdteT6rBjQxhZ+0en5J9Q2EqMcvTmbOBggRTujfoxRQX
# lIpJof1dtL7s+9gWT/GOnSrPRsDcw/Y5GCK6OJfJXONlX51y8QeFvc4KyOCQBZI7
# lWsJn12h3NXodJDPHg6cwb8uMct0rnOK+eoQdigJxqiju9S4V6VjFKq+d03e0ego
# V+35tIxbmSIq69yxRJuk3wDL284kH0eg9G3b/omsksgay3ji38nZkmFvknN5gdjS
# I5EzOonogpdnwIuI46XPqwIySOw7xG8rIB5iS5GOzRtnC8+FkOh8mPllJTZQJfVc
# rzK8bp7dQFT8L6kUrKOg7ielnZCMW/p9FZLOO22siNBigDpMXrpcZbxLBh+p/Pux
# U2AfH3vTr5rnlMvI0S9mFJ7HCHJ07zBLIAx7
# SIG # End signature block