Tests/Unit/Get-SPCBrokenInheritance.Tests.ps1

#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }

BeforeAll {
    . (Join-Path $PSScriptRoot '../../Private/PnPWrappers.ps1')
    . (Join-Path $PSScriptRoot '../../Private/Test-SPCConnection.ps1')
    . (Join-Path $PSScriptRoot '../../Private/Connect-SPCSiteInternal.ps1')
    . (Join-Path $PSScriptRoot '../../Private/Invoke-SPCTempElevationInternal.ps1')
    . (Join-Path $PSScriptRoot '../../Private/Get-SPCLibraryBrokenInheritanceInternal.ps1')
    . (Join-Path $PSScriptRoot '../../Public/Scan/Get-SPCBrokenInheritance.ps1')
}

Describe 'Get-SPCBrokenInheritance Unit Tests' -Tag 'Scan', 'Permissions', 'Governance' {
    BeforeEach {
        $script:SPCContext = [PSCustomObject]@{
            TenantName       = 'contoso'
            OperatorUPN      = 'admin@contoso.onmicrosoft.com'
            AuthMode         = 'Interactive'
            GraphAccessToken = 'mock_token'
            PnPContext       = [PSCustomObject]@{ Url = 'https://contoso-admin.sharepoint.com' }
        }
    }

    Context 'Unique Role Assignments (Broken Inheritance) Discovery' {
        It 'Should detect broken inheritance on Document Libraries and return structured object' {
            Mock Test-SPCConnection { $true }
            Mock Connect-SPCSiteInternal { [PSCustomObject]@{ Url = 'https://contoso.sharepoint.com/sites/Finance' } }
            Mock Get-PnPList {
                @(
                    [PSCustomObject]@{
                        Title                    = 'Restricted Invoices'
                        BaseType                 = 'DocumentLibrary'
                        HasUniqueRoleAssignments = $true
                        Hidden                   = $false
                        RootFolder               = [PSCustomObject]@{ ServerRelativeUrl = '/sites/Finance/Invoices' }
                    }
                )
            }
            Mock Get-PnPListItem {
                @(
                    [PSCustomObject]@{
                        Id                       = 1
                        HasUniqueRoleAssignments = $true
                        FileSystemObjectType     = 'File'
                        FieldValues              = @{ 'FileRef' = '/sites/Finance/Invoices/Inv001.pdf' }
                    }
                )
            }

            $result = Get-SPCBrokenInheritance -SiteUrl 'https://contoso.sharepoint.com/sites/Finance'

            $result | Should -Not -BeNullOrEmpty
            $result.SiteUrl | Should -Be 'https://contoso.sharepoint.com/sites/Finance'
            $result.UniqueScopes | Should -Be 2
            $result.BrokenItems.Count | Should -Be 2
            $result.BrokenItems[0].Type | Should -Be 'Library'
            $result.BrokenItems[1].Type | Should -Be 'File/Item'
        }
    }

    Context 'Threshold and Boundary Handling' {
        It 'Should return 0 unique scopes when inheritance is fully intact across libraries' {
            Mock Test-SPCConnection { $true }
            Mock Connect-SPCSiteInternal { [PSCustomObject]@{} }
            Mock Get-PnPList {
                @(
                    [PSCustomObject]@{
                        Title                    = 'Shared Documents'
                        BaseType                 = 'DocumentLibrary'
                        HasUniqueRoleAssignments = $false
                        Hidden                   = $false
                        RootFolder               = [PSCustomObject]@{ ServerRelativeUrl = '/sites/Finance/Shared Documents' }
                    }
                )
            }
            Mock Get-PnPListItem { @() }

            $result = Get-SPCBrokenInheritance -SiteUrl 'https://contoso.sharepoint.com/sites/Finance'

            $result.UniqueScopes | Should -Be 0
            $result.BrokenItems.Count | Should -Be 0
        }
    }

    Context 'Connection Guard and Error Handling' {
        It 'Should throw ERR-001 when not connected' {
            Mock Test-SPCConnection { throw 'Not connected' }

            { Get-SPCBrokenInheritance -SiteUrl 'https://contoso.sharepoint.com/sites/Finance' } |
                Should -Throw -ExpectedMessage '*ERR-001*'
        }

        It 'AC-SCA-01 Should temporarily elevate SCA on Access Denied when -AddTempSiteCollectionAdmin is supplied' {
            Mock Test-SPCConnection { $true }
            $script:accessDenied = $true
            Mock Connect-SPCSiteInternal {
                return [PSCustomObject]@{ Url = 'https://contoso.sharepoint.com/sites/Finance' }
            }
            Mock Get-PnPList {
                if ($script:accessDenied) {
                    throw "403 Unauthorized: Access Denied"
                }
                return @(
                    [PSCustomObject]@{
                        Title                    = 'Restricted Docs'
                        BaseType                 = 'DocumentLibrary'
                        HasUniqueRoleAssignments = $true
                        Hidden                   = $false
                        RootFolder               = [PSCustomObject]@{ ServerRelativeUrl = '/sites/Finance/Restricted' }
                    }
                )
            }
            Mock Get-PnPListItem { @() }
            Mock Set-PnPTenantSite {
                $script:accessDenied = $false
            }
            Mock Remove-PnPSiteCollectionAdmin {}
            Mock Start-Sleep {}

            $result = Get-SPCBrokenInheritance -SiteUrl 'https://contoso.sharepoint.com/sites/Finance' -AddTempSiteCollectionAdmin
            $result.UniqueScopes | Should -Be 1
            Should -Invoke -CommandName Set-PnPTenantSite -Times 1 -Exactly
            Should -Invoke -CommandName Remove-PnPSiteCollectionAdmin -Times 1 -Exactly
        }
    }
}