Testing/Unit/PowerShell/Support/Set-CyberAssessmentAppPermission.Tests.ps1

$ModulesPath = "../../../../Modules"
$ServicePrincipalModule = "$($ModulesPath)/Support/ServicePrincipal.psm1"
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath $ServicePrincipalModule)

InModuleScope ServicePrincipal {
    Describe "Set-CyberAssessmentAppPermission" {
        BeforeAll {
            Mock -CommandName Connect-GraphHelper -MockWith {
                return $null
            }
        }

        It "Verify no changes are needed for optimal permissions" {
            $OptimalInputObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockOptimalPermissions.json") | ConvertFrom-Json
            $Optimal = $OptimalInputObject | Set-CyberAssessmentAppPermission -WhatIf
            $Optimal | Should -Be "No changes needed - service principal is already configured correctly."
        }

        It "Verify changes would be made if missing permissions are detected" {
            $MissingPermissionObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockMissingPermission.json") | ConvertFrom-Json
            $MissingPermissionObject.MissingPermissions.Permission = 'Policy.Read.All'
            $Result = $MissingPermissionObject | Set-CyberAssessmentAppPermission -WhatIf
            $Result[0] | Should -Be 'WhatIf: Would add missing permissions: Policy.Read.All'
        }

        Context "No Current Permissions Tests" {
            BeforeAll {
                # Mock Get-CyberAssessmentPermissions for delegated permissions removal
                Mock -CommandName Get-CyberAssessmentPermissions -MockWith {
                    param($outAs, $id)
                    if ($outAs -eq 'api') {
                        return "https://graph.microsoft.com/v1.0/servicePrincipals/$id/oauth2PermissionGrants"
                    }
                    return $null
                }

                # Mock Invoke-GraphDirectly for role definition lookup (if MissingRoles exists)
                Mock -CommandName Invoke-GraphDirectly -MockWith {
                    param($Commandlet, $queryParams)

                    if ($Commandlet -eq 'Get-MgRoleManagementDirectoryRoleDefinition' -and $queryParams) {
                        return [PSCustomObject]@{
                            Value = @(
                                [PSCustomObject]@{
                                    IsBuiltIn = $true
                                    DisplayName = "Global Reader"
                                    TemplateId = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
                                    Id = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
                                }
                            )
                        }
                    }
                    return $null
                }
            }

            It "Verify needed permissions would be added, unneeded delegated permissions removed, Global Reader role added, and power platform registered" {
                $NoCurrentPermissionsObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockNoCurrentPermissions.json") | ConvertFrom-Json
                $Result = $NoCurrentPermissionsObject | Set-CyberAssessmentAppPermission -WhatIf

                # Verify the message contains expected delegated permissions removal
                $Result[0] | Should -Be "WhatIf: Would remove delegated permissions: User.Read"

                # Verify the message contains expected missing permissions
                $Result[1] | Should -Be "WhatIf: Would add missing permissions: PrivilegedAccess.Read.AzureADGroup, Policy.Read.All, PrivilegedEligibilitySchedule.Read.AzureADGroup, RoleManagementPolicy.Read.AzureADGroup, Directory.Read.All, RoleManagement.Read.Directory, User.Read.All, Exchange.ManageAsApp, Sites.FullControl.All"

                # Verify the correct role is assigned
                $Result[2] | Should -Be "WhatIf: Would assign directory role 'Global Reader' to 00000000-0000-0000-0000-000000000001"

                # Verify that Power Platform registration is handled
                $Result[3] | Should -Be "WhatIf: Would register application with Power Platform"
            }
        }

        Context "Missing Roles Tests" {
            BeforeAll {
                # Mock only the functions needed for role assignment tests
                Mock -CommandName Invoke-GraphDirectly -MockWith {
                    param($Commandlet, $queryParams)

                    if ($Commandlet -eq 'Get-MgRoleManagementDirectoryRoleDefinition' -and $queryParams) {
                        # Mock role definition lookup - return structure that matches Graph API
                        # The real API returns an object with a Value property containing an array
                        return [PSCustomObject]@{
                            Value = @(
                                [PSCustomObject]@{
                                    IsBuiltIn = $true
                                    DisplayName = "Global Reader"
                                    TemplateId = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
                                    RolePermissions = @(@{
                                        Condition = $null
                                        AllowedResourceActions = @()
                                    })
                                    Version = "1"
                                    Id = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
                                    Description = ""
                                    IsEnabled = $true
                                    InheritsPermissionsFrom = @(@{Id = "88d8e3e3-8f55-4a1e-953a-9b9898b8876b"})
                                    ResourceScopes = @("/")
                                }
                            )
                        }
                    }
                    return $null
                }
            }

            It "Verify changes would be made if missing roles are detected" {
                $MissingRoleObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockMissingRole.json") | ConvertFrom-Json
                $MissingRoleObject.MissingRoles = 'Global Reader'
                $Result = $MissingRoleObject | Set-CyberAssessmentAppPermission -WhatIf
                $Result[0] | Should -Match "WhatIf: Would assign directory role 'Global Reader' to 00000000-0000-0000-0000-000000000001"
            }
        }

        Context "Extra Permissions Tests" {

            It "Verify changes would be made if extra permissions are detected" {
                $ExtraPermissionObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockExtraPermissions.json") | ConvertFrom-Json
                $ExtraPermissionInputObject = $ExtraPermissionObject
                $Result = $ExtraPermissionInputObject | Set-CyberAssessmentAppPermission -WhatIf
                $Result[0] | Should -Be "WhatIf: Would remove extra permissions: Acronym.Read.All"
            }
        }

        Context "Power Platform Registration Tests" {

            It "Verify Power Platform would be removed when registered but not needed" {
                $PowerPlatformUnneededObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockPowerPlatformUnneeded.json") | ConvertFrom-Json
                $Result = $PowerPlatformUnneededObject | Set-CyberAssessmentAppPermission -WhatIf
                $Result[0] | Should -Be "WhatIf: Would remove Power Platform registration"
            }

            It "Verify Power Platform would be registered when needed but missing" {
                $PowerPlatformNeededObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockPowerPlatformNeeded.json") | ConvertFrom-Json
                $Result = $PowerPlatformNeededObject | Set-CyberAssessmentAppPermission -WhatIf
                $Result[0] | Should -Be "WhatIf: Would register application with Power Platform"
            }
        }

        Context "Multiple Issues Tests" {
            BeforeAll {
                $MultipleIssuesObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockMultipleIssues.json") | ConvertFrom-Json

                # Mock all functions needed for multiple issues scenario
                Mock -CommandName Get-CyberAssessmentPermissions -MockWith {
                    param($outAs, $id)
                    if ($outAs -eq 'api') {
                        return "https://graph.microsoft.com/v1.0/servicePrincipals/$id/appRoleAssignments"
                    }
                    return $null
                }

                Mock -CommandName Invoke-GraphDirectly -MockWith {
                    param($Commandlet, $queryParams)

                    if ($Commandlet -eq 'Get-MgBetaApplication') {
                        return $MultipleIssuesObject.ExtraPermissions
                    }
                    elseif ($Commandlet -eq 'Update-MgBetaApplication') {
                        return $null
                    }
                    elseif ($Commandlet -eq 'Get-MgRoleManagementDirectoryRoleDefinition' -and $queryParams) {
                        return [PSCustomObject]@{
                            Value = @(
                                [PSCustomObject]@{
                                    Id = "f2ef992c-3afb-46b9-b7cf-a126ee74c451"
                                    DisplayName = "Global Reader"
                                }
                            )
                        }
                    }
                    return $null
                }
            }

            It "Verify multiple issues are detected and reported" {
                $MultipleIssuesInputObject = $MultipleIssuesObject
                $Result = $MultipleIssuesInputObject | Set-CyberAssessmentAppPermission -WhatIf

                # Verify all expected messages are present
                $Result[0] | Should -Be "WhatIf: Would remove extra permissions: Acronym.Read.All"
                $Result[1] | Should -Be "WhatIf: Would add missing permissions: Policy.Read.All"
                $Result[2] | Should -Be "WhatIf: Would assign directory role 'Global Reader' to 00000000-0000-0000-0000-000000000001"
            }
        }

        Context "Handle permissions in App Registration Manifest that are not granted admin consent" {

            It "Verify permissions in manifest that only need to be granted admin consent" {
                $MissingAdminConsentPermissionsObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockManifestMissingAdminConsent.json") | ConvertFrom-Json
                $Result = $MissingAdminConsentPermissionsObject | Set-CyberAssessmentAppPermission -WhatIf

                # Verify expected message is present
                $Result[0] | Should -Be "WhatIf: Would add missing permissions: User.Read.All, Exchange.ManageAsApp"
            }
        }

        Context "Verify both exchange permissions are present in GCC High environments" {

            It "Verify Microsoft Exchange Online Protection Exchange.ManageAsApp permission is handled correctly in GCC High" {
                $MissingAdminConsentPermissionsObject = Get-Content (Join-Path -Path $PSScriptRoot -ChildPath "SetCyberAssessmentAppPermissionData\MockMissingGCCHighExoPermission.json") | ConvertFrom-Json
                $Result = $MissingAdminConsentPermissionsObject | Set-CyberAssessmentAppPermission -WhatIf

                # Verify expected message is present
                $Result[0] | Should -Be "WhatIf: Would add missing permissions: Exchange.ManageAsApp"
            }
        }
    }
}

AfterAll {
    Remove-Module ServicePrincipal -Force -ErrorAction 'SilentlyContinue'
}