Tests/Get-O365MailboxUsage.Tests.ps1

#Requires -Module Pester

<#
.SYNOPSIS
    Pester tests for Get-O365MailboxUsage function
 
.DESCRIPTION
    Comprehensive unit tests for the Get-O365MailboxUsage function including:
    - Parameter validation
    - Helper function testing
    - Mock testing for Exchange Online cmdlets
    - Pipeline input testing
    - Sorting functionality
    - Error handling
 
.NOTES
    Run with: Invoke-Pester -Path ".\Get-O365MailboxUsage.Tests.ps1" -Verbose
    Requires Pester module: Install-Module -Name Pester -Force -SkipPublisherCheck
#>


BeforeAll {
    # Import the functions to test
    $functionPath = Join-Path $PSScriptRoot "..\Public\Get-O365MailboxUsage.ps1"
    $connectionPath = Join-Path $PSScriptRoot "..\Public\Connect-O365Exchange.ps1"
    . $functionPath
    . $connectionPath
    
    # Mock Exchange Online cmdlets since we can't connect to real Exchange Online in tests
    Mock Get-ConnectionInformation {
        return @{
            Name = "TestConnection"
            State = "Connected"
        }
    }
    
    # Create mock mailbox objects
    $MockMailbox1 = [PSCustomObject]@{
        DisplayName = "John Doe"
        PrimarySmtpAddress = "john.doe@company.com"
        RecipientTypeDetails = "UserMailbox"
        ArchiveStatus = "Active"
    }
    
    $MockMailbox2 = [PSCustomObject]@{
        DisplayName = "Jane Smith"
        PrimarySmtpAddress = "jane.smith@company.com"
        RecipientTypeDetails = "UserMailbox"
        ArchiveStatus = "None"
    }
    
    $MockMailbox3 = [PSCustomObject]@{
        DisplayName = "Shared Resources"
        PrimarySmtpAddress = "shared@company.com"
        RecipientTypeDetails = "SharedMailbox"
        ArchiveStatus = "None"
    }
    
    # Create mock mailbox statistics
    $MockStats1 = [PSCustomObject]@{
        TotalItemSize = "25 GB (26,843,545,600 bytes)"
        ProhibitSendReceiveQuota = "50 GB (53,687,091,200 bytes)"
        ItemCount = 15000
        LastLogonTime = (Get-Date).AddDays(-1)
    }
    
    $MockStats2 = [PSCustomObject]@{
        TotalItemSize = "45 GB (48,318,382,080 bytes)"
        ProhibitSendReceiveQuota = "50 GB (53,687,091,200 bytes)"
        ItemCount = 28000
        LastLogonTime = (Get-Date).AddDays(-3)
    }
    
    $MockStats3 = [PSCustomObject]@{
        TotalItemSize = "5 GB (5,368,709,120 bytes)"
        ProhibitSendReceiveQuota = "25 GB (26,843,545,600 bytes)"
        ItemCount = 3500
        LastLogonTime = (Get-Date).AddDays(-7)
    }
    
    # Create mock archive statistics
    $MockArchiveStats1 = [PSCustomObject]@{
        TotalItemSize = "10 GB (10,737,418,240 bytes)"
        ProhibitSendReceiveQuota = "100 GB (107,374,182,400 bytes)"
        ItemCount = 5000
    }
}

Describe "Get-O365MailboxUsage" {
    
    Context "Parameter Validation" {
        It "Should accept valid SortBy parameters" {
            $validSortBy = @("PercentFull", "UsedSpaceGB", "TotalQuotaGB", "DisplayName")
            foreach ($sortBy in $validSortBy) {
                { Get-O365MailboxUsage -SortBy $sortBy -WhatIf } | Should -Not -Throw
            }
        }
        
        It "Should reject invalid SortBy parameters" {
            { Get-O365MailboxUsage -SortBy "InvalidSort" } | Should -Throw
        }
        
        It "Should accept pipeline input for UserPrincipalName" {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { return $MockStats1 }
            
            { "john.doe@company.com" | Get-O365MailboxUsage } | Should -Not -Throw
        }
        
        It "Should accept array input for UserPrincipalName" {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { return $MockStats1 }
            
            { Get-O365MailboxUsage -UserPrincipalName @("john.doe@company.com", "jane.smith@company.com") } | Should -Not -Throw
        }
    }
    
    Context "Connection Testing" {
        It "Should check Exchange Online connection" {
            Mock Get-ConnectionInformation { throw "Not connected" }
            
            { Get-O365MailboxUsage } | Should -Throw "*Exchange Online connection required*"
        }
        
        It "Should proceed when properly connected" {
            Mock Get-ConnectionInformation { return @{ Name = "TestConnection" } }
            Mock Get-Mailbox { return @() }
            
            { Get-O365MailboxUsage } | Should -Not -Throw
        }
    }
    
    Context "Single User Processing" {
        BeforeEach {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { return $MockStats1 }
        }
        
        It "Should process a single user correctly" {
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result | Should -Not -BeNullOrEmpty
            $result.DisplayName | Should -Be "John Doe"
            $result.UserPrincipalName | Should -Be "john.doe@company.com"
            $result.UsedSpaceGB | Should -Be 25
            $result.TotalQuotaGB | Should -Be 50
            $result.PercentFull | Should -Be 50.0
        }
        
        It "Should include archive statistics when requested" {
            Mock Get-MailboxStatistics -ParameterFilter { $Archive } { return $MockArchiveStats1 }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com" -IncludeArchive
            
            $result.ArchiveUsedSpaceGB | Should -Be 10
            $result.ArchiveTotalQuotaGB | Should -Be 100
            $result.ArchivePercentFull | Should -Be 10.0
        }
    }
    
    Context "Multiple User Processing" {
        BeforeEach {
            Mock Get-Mailbox -ParameterFilter { $ResultSize -eq "Unlimited" } {
                return @($MockMailbox1, $MockMailbox2, $MockMailbox3)
            }
            Mock Get-MailboxStatistics -ParameterFilter { $Identity -eq "john.doe@company.com" } { return $MockStats1 }
            Mock Get-MailboxStatistics -ParameterFilter { $Identity -eq "jane.smith@company.com" } { return $MockStats2 }
            Mock Get-MailboxStatistics -ParameterFilter { $Identity -eq "shared@company.com" } { return $MockStats3 }
        }
        
        It "Should process all mailboxes when no specific user provided" {
            $results = Get-O365MailboxUsage
            
            $results | Should -HaveCount 3
            $results[0].DisplayName | Should -Be "Jane Smith"  # Should be sorted by PercentFull descending
            $results[1].DisplayName | Should -Be "John Doe"
            $results[2].DisplayName | Should -Be "Shared Resources"
        }
        
        It "Should sort by PercentFull descending by default" {
            $results = Get-O365MailboxUsage
            
            $results[0].PercentFull | Should -BeGreaterThan $results[1].PercentFull
            $results[1].PercentFull | Should -BeGreaterThan $results[2].PercentFull
        }
        
        It "Should sort by DisplayName when requested" {
            $results = Get-O365MailboxUsage -SortBy DisplayName
            
            $results[0].DisplayName | Should -Be "Jane Smith"  # Alphabetical order
            $results[1].DisplayName | Should -Be "John Doe"
            $results[2].DisplayName | Should -Be "Shared Resources"
        }
        
        It "Should sort by UsedSpaceGB when requested" {
            $results = Get-O365MailboxUsage -SortBy UsedSpaceGB
            
            $results[0].UsedSpaceGB | Should -BeGreaterOrEqual $results[1].UsedSpaceGB
            $results[1].UsedSpaceGB | Should -BeGreaterOrEqual $results[2].UsedSpaceGB
        }
    }
    
    Context "Pipeline Input Processing" {
        It "Should accept single user from pipeline" {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { return $MockStats1 }
            
            $result = "john.doe@company.com" | Get-O365MailboxUsage
            
            $result | Should -Not -BeNullOrEmpty
            $result.UserPrincipalName | Should -Be "john.doe@company.com"
        }
        
        It "Should accept multiple users from pipeline" {
            Mock Get-Mailbox -ParameterFilter { $Identity -eq "john.doe@company.com" } { return $MockMailbox1 }
            Mock Get-Mailbox -ParameterFilter { $Identity -eq "jane.smith@company.com" } { return $MockMailbox2 }
            Mock Get-MailboxStatistics -ParameterFilter { $Identity -eq "john.doe@company.com" } { return $MockStats1 }
            Mock Get-MailboxStatistics -ParameterFilter { $Identity -eq "jane.smith@company.com" } { return $MockStats2 }
            
            $results = @("john.doe@company.com", "jane.smith@company.com") | Get-O365MailboxUsage
            
            $results | Should -HaveCount 2
            $results[0].UserPrincipalName | Should -Be "jane.smith@company.com"  # Sorted by PercentFull
            $results[1].UserPrincipalName | Should -Be "john.doe@company.com"
        }
    }
    
    Context "Helper Function Testing - ConvertTo-GB" {
        It "Should convert GB sizes correctly" {
            # This tests the internal ConvertTo-GB function indirectly
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { 
                return [PSCustomObject]@{
                    TotalItemSize = "25 GB (26,843,545,600 bytes)"
                    ProhibitSendReceiveQuota = "50 GB (53,687,091,200 bytes)"
                    ItemCount = 15000
                    LastLogonTime = (Get-Date)
                }
            }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result.UsedSpaceGB | Should -Be 25
            $result.TotalQuotaGB | Should -Be 50
        }
        
        It "Should handle MB to GB conversion" {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { 
                return [PSCustomObject]@{
                    TotalItemSize = "1024 MB (1,073,741,824 bytes)"
                    ProhibitSendReceiveQuota = "2048 MB (2,147,483,648 bytes)"
                    ItemCount = 5000
                    LastLogonTime = (Get-Date)
                }
            }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result.UsedSpaceGB | Should -Be 1
            $result.TotalQuotaGB | Should -Be 2
        }
    }
    
    Context "Error Handling" {
        It "Should handle mailbox not found gracefully" {
            Mock Get-Mailbox { throw "Mailbox not found" }
            
            { Get-O365MailboxUsage -UserPrincipalName "nonexistent@company.com" } | Should -Throw
        }
        
        It "Should handle statistics retrieval failure gracefully" {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { throw "Statistics not available" }
            
            { Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com" } | Should -Throw
        }
        
        It "Should warn when no results are returned" {
            Mock Get-Mailbox { return @() }
            
            $result = Get-O365MailboxUsage
            $result | Should -BeNullOrEmpty
        }
    }
    
    Context "Archive Functionality" {
        It "Should skip archive when mailbox has no archive" {
            Mock Get-Mailbox { return $MockMailbox2 }  # ArchiveStatus = "None"
            Mock Get-MailboxStatistics { return $MockStats2 }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "jane.smith@company.com" -IncludeArchive
            
            $result.ArchiveUsedSpaceGB | Should -BeNullOrEmpty
        }
        
        It "Should include archive when requested and available" {
            Mock Get-Mailbox { return $MockMailbox1 }  # ArchiveStatus = "Active"
            Mock Get-MailboxStatistics -ParameterFilter { -not $Archive } { return $MockStats1 }
            Mock Get-MailboxStatistics -ParameterFilter { $Archive } { return $MockArchiveStats1 }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com" -IncludeArchive
            
            $result.ArchiveUsedSpaceGB | Should -Be 10
            $result.ArchiveTotalQuotaGB | Should -Be 100
            $result.ArchivePercentFull | Should -Be 10.0
            $result.ArchiveItemCount | Should -Be 5000
        }
    }
    
    Context "Output Validation" {
        BeforeEach {
            Mock Get-Mailbox { return $MockMailbox1 }
            Mock Get-MailboxStatistics { return $MockStats1 }
        }
        
        It "Should return objects with correct properties" {
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result.PSObject.Properties.Name | Should -Contain "DisplayName"
            $result.PSObject.Properties.Name | Should -Contain "UserPrincipalName"
            $result.PSObject.Properties.Name | Should -Contain "UsedSpaceGB"
            $result.PSObject.Properties.Name | Should -Contain "TotalQuotaGB"
            $result.PSObject.Properties.Name | Should -Contain "PercentFull"
            $result.PSObject.Properties.Name | Should -Contain "ItemCount"
            $result.PSObject.Properties.Name | Should -Contain "LastLogonTime"
            $result.PSObject.Properties.Name | Should -Contain "MailboxType"
        }
        
        It "Should calculate percentage correctly" {
            # 25 GB used out of 50 GB total = 50%
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result.PercentFull | Should -Be 50.0
        }
        
        It "Should handle division by zero for percentage calculation" {
            Mock Get-MailboxStatistics { 
                return [PSCustomObject]@{
                    TotalItemSize = "25 GB (26,843,545,600 bytes)"
                    ProhibitSendReceiveQuota = "Unlimited"
                    ItemCount = 15000
                    LastLogonTime = (Get-Date)
                }
            }
            
            $result = Get-O365MailboxUsage -UserPrincipalName "john.doe@company.com"
            
            $result.PercentFull | Should -Be 0
        }
    }
}

Describe "Connect-O365Exchange" {
    
    Context "Parameter Validation" {
        It "Should accept valid email addresses for UserPrincipalName" {
            { Connect-O365Exchange -UserPrincipalName "user@domain.com" -WhatIf } | Should -Not -Throw
        }
        
        It "Should reject invalid email addresses" {
            { Connect-O365Exchange -UserPrincipalName "invalid-email" } | Should -Throw
        }
        
        It "Should accept organization parameter" {
            { Connect-O365Exchange -Organization "contoso" -WhatIf } | Should -Not -Throw
        }
    }
    
    Context "Module Dependency" {
        It "Should handle missing ExchangeOnlineManagement module gracefully" {
            Mock Get-Module { return $null }
            Mock Read-Host { return "N" }
            
            $result = Connect-O365Exchange
            $result | Should -Be $false
        }
    }
    
    Context "Connection Management" {
        It "Should detect existing connections" {
            Mock Get-ConnectionInformation { return @{ Name = "TestTenant"; State = "Connected" } }
            Mock Get-OrganizationConfig { return @{} }
            
            $result = Connect-O365Exchange
            $result | Should -Be $true
        }
        
        It "Should handle connection failures gracefully" {
            Mock Get-ConnectionInformation { throw "Connection failed" }
            Mock Connect-ExchangeOnline { throw "Authentication failed" }
            
            $result = Connect-O365Exchange
            $result | Should -Be $false
        }
    }
}

Describe "Test-O365ExchangeConnection" {
    
    Context "Connection Status Testing" {
        It "Should return true for active connection" {
            Mock Get-ConnectionInformation { return @{ Name = "TestTenant"; State = "Connected" } }
            Mock Get-OrganizationConfig { return @{} }
            
            $result = Test-O365ExchangeConnection -Quiet
            $result | Should -Be $true
        }
        
        It "Should return false for no connection" {
            Mock Get-ConnectionInformation { return $null }
            
            $result = Test-O365ExchangeConnection -Quiet
            $result | Should -Be $false
        }
        
        It "Should return false for non-functional connection" {
            Mock Get-ConnectionInformation { return @{ Name = "TestTenant"; State = "Connected" } }
            Mock Get-OrganizationConfig { throw "Connection not functional" }
            
            $result = Test-O365ExchangeConnection -Quiet
            $result | Should -Be $false
        }
    }
    
    Context "Output Modes" {
        It "Should provide detailed information when requested" {
            Mock Get-ConnectionInformation { 
                return @{ 
                    Name = "TestTenant"
                    UserPrincipalName = "test@domain.com"
                    State = "Connected"
                    TokenExpiryTimeUTC = (Get-Date).AddHours(1)
                } 
            }
            Mock Get-OrganizationConfig { return @{} }
            
            { Test-O365ExchangeConnection -Detailed } | Should -Not -Throw
        }
        
        It "Should suppress output in quiet mode" {
            Mock Get-ConnectionInformation { return $null }
            
            { Test-O365ExchangeConnection -Quiet } | Should -Not -Throw
        }
    }
}