Tests/PSFloRecruit.Tests.ps1

#Requires -Modules Pester

BeforeAll {
    $ModulePath = Split-Path -Parent $PSScriptRoot
    Import-Module $ModulePath -Force
}

Describe 'PSFloRecruit Module' {
    Context 'Module Structure' {
        It 'Should have a valid manifest' {
            $manifestPath = Join-Path (Split-Path -Parent $PSScriptRoot) 'PSFloRecruit.psd1'
            Test-Path $manifestPath | Should -Be $true

            $manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction SilentlyContinue
            $manifest | Should -Not -BeNullOrEmpty
        }

        It 'Should export Connect-FloRecruit' {
            Get-Command Connect-FloRecruit -Module PSFloRecruit -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It 'Should export Disconnect-FloRecruit' {
            Get-Command Disconnect-FloRecruit -Module PSFloRecruit -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It 'Should export Get-FloRecruitSession' {
            Get-Command Get-FloRecruitSession -Module PSFloRecruit -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It 'Should export Get-FloRecruitJobApplication' {
            Get-Command Get-FloRecruitJobApplication -Module PSFloRecruit -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }
    }
}

Describe 'Connect-FloRecruit' {
    BeforeAll {
        Mock Invoke-WebRequest {
            param($Uri, $Method, $Body, $ContentType, $SessionVariable)

            # Create a mock response with proper headers
            $response = [PSCustomObject]@{
                StatusCode = 200
                Headers = @{
                    'Set-Cookie' = 'auth_token=mock-token-12345; Path=/; HttpOnly'
                }
                Content = '{}'
            }

            return $response
        } -ModuleName PSFloRecruit
    }

    AfterEach {
        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession = $null
        }
    }

    It 'Should connect with SecureString password' {
        $password = ConvertTo-SecureString 'TestPassword123!' -AsPlainText -Force
        $result = Connect-FloRecruit -OrganizationName 'testorg' -Email 'admin@test.com' -Password $password

        $result | Should -Not -BeNullOrEmpty
        $result.OrganizationName | Should -Be 'testorg'
        $result.Email | Should -Be 'admin@test.com'
    }

    It 'Should create a session object' {
        $password = ConvertTo-SecureString 'TestPassword123!' -AsPlainText -Force
        Connect-FloRecruit -OrganizationName 'testorg' -Email 'admin@test.com' -Password $password

        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession | Should -Not -BeNullOrEmpty
            $Script:FloRecruitSession.OrganizationName | Should -Be 'testorg'
            $Script:FloRecruitSession.Email | Should -Be 'admin@test.com'
            $Script:FloRecruitSession.AuthToken | Should -Not -BeNullOrEmpty
        }
    }

    It 'Should accept plain text password' {
        $result = Connect-FloRecruit -OrganizationName 'testorg' -Email 'admin@test.com' -PasswordPlainText 'TestPassword123!'

        $result | Should -Not -BeNullOrEmpty
    }
}

Describe 'Disconnect-FloRecruit' {
    BeforeEach {
        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession = [PSCustomObject]@{
                Email = 'test@test.com'
            }
        }
    }

    It 'Should clear the session' {
        Disconnect-FloRecruit

        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession | Should -BeNullOrEmpty
        }
    }

    It 'Should not error when no session exists' {
        Disconnect-FloRecruit
        { Disconnect-FloRecruit } | Should -Not -Throw
    }
}

Describe 'Get-FloRecruitSession' {
    Context 'When connected' {
        BeforeAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = [PSCustomObject]@{
                    OrganizationName = 'testorg'
                    Email = 'admin@test.com'
                    BaseUrl = 'https://florecruit.com/app/testorg/api'
                    ConnectedAt = Get-Date
                    TokenExpiry = (Get-Date).AddHours(1)
                    RequestCount = 5
                    RateLimitWindowStart = Get-Date
                }
            }
        }

        AfterAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = $null
            }
        }

        It 'Should return session information' {
            $session = Get-FloRecruitSession

            $session | Should -Not -BeNullOrEmpty
            $session.OrganizationName | Should -Be 'testorg'
            $session.Email | Should -Be 'admin@test.com'
        }

        It 'Should not include credentials' {
            $session = Get-FloRecruitSession

            $session.Password | Should -BeNullOrEmpty
            $session.MFASecret | Should -BeNullOrEmpty
        }

        It 'Should include rate limit information' {
            $session = Get-FloRecruitSession

            $session.RequestsInWindow | Should -Be 5
            $session.RequestsRemaining | Should -BeGreaterThan 0
        }
    }

    Context 'When not connected' {
        BeforeAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = $null
            }
        }

        It 'Should return null' {
            $session = Get-FloRecruitSession
            $session | Should -BeNullOrEmpty
        }
    }
}

Describe 'Get-FloRecruitJobApplication' {
    BeforeAll {
        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession = [PSCustomObject]@{
                OrganizationName = 'testorg'
                Email = 'admin@test.com'
                BaseUrl = 'https://florecruit.com/app/testorg/api'
                AuthToken = 'mock-token'
                TokenExpiry = (Get-Date).AddHours(1)
                RequestCount = 0
                RateLimitWindowStart = Get-Date
            }
        }

        $mockJobApp1 = @{
            applicationId = 'app123'
            candidateId = 'cand456'
            firstName = 'John'
            lastName = 'Doe'
            email = 'john.doe@example.com'
            jobTitle = 'Software Engineer'
            status = 'Hired'
        }

        $mockJobApp2 = @{
            applicationId = 'app124'
            candidateId = 'cand457'
            firstName = 'Jane'
            lastName = 'Smith'
            email = 'jane.smith@example.com'
            jobTitle = 'Product Manager'
            status = 'Offered'
        }

        Mock Invoke-WebRequest {
            return [PSCustomObject]@{
                Content = (ConvertTo-Json @($mockJobApp1, $mockJobApp2))
                StatusCode = 200
                Headers = @{}
            }
        } -ModuleName PSFloRecruit
    }

    AfterAll {
        InModuleScope PSFloRecruit {
            $Script:FloRecruitSession = $null
        }
    }

    It 'Should retrieve job applications' {
        $result = Get-FloRecruitJobApplication
        $result | Should -Not -BeNullOrEmpty
        $result.Count | Should -BeGreaterThan 0
    }

    It 'Should have correct type name' {
        $result = Get-FloRecruitJobApplication
        $result[0].PSObject.TypeNames | Should -Contain 'FloRecruit.JobApplication'
    }

    It 'Should accept status parameter' {
        { Get-FloRecruitJobApplication -Status "Hired" } | Should -Not -Throw
    }

    It 'Should accept status bucket parameter' {
        { Get-FloRecruitJobApplication -StatusBucket "Hired" } | Should -Not -Throw
    }

    It 'Should accept date range parameters' {
        $start = Get-Date "2026-01-01"
        $end = Get-Date "2026-01-31"
        { Get-FloRecruitJobApplication -StatusStart $start -StatusEnd $end } | Should -Not -Throw
    }

    It 'Should accept limit parameter' {
        { Get-FloRecruitJobApplication -Limit 100 } | Should -Not -Throw
    }
}

Describe 'Error Handling' {
    Context 'When not connected' {
        BeforeAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = $null
            }
        }

        It 'Should throw error when calling Get-FloRecruitJobApplication' {
            { Get-FloRecruitJobApplication } | Should -Throw "*Not connected*"
        }
    }

    Context 'When API returns error' {
        BeforeAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = [PSCustomObject]@{
                    OrganizationName = 'testorg'
                    BaseUrl = 'https://florecruit.com/app/testorg/api'
                    AuthToken = 'mock-token'
                    TokenExpiry = (Get-Date).AddHours(1)
                    RequestCount = 0
                    RateLimitWindowStart = Get-Date
                }
            }

            Mock Invoke-WebRequest {
                $response = New-Object System.Net.Http.HttpResponseMessage
                $response.StatusCode = [System.Net.HttpStatusCode]::Unauthorized
                throw [Microsoft.PowerShell.Commands.HttpResponseException]::new("Unauthorized", $response)
            } -ModuleName PSFloRecruit
        }

        AfterAll {
            InModuleScope PSFloRecruit {
                $Script:FloRecruitSession = $null
            }
        }

        It 'Should handle 401 error' {
            { Get-FloRecruitJobApplication } | Should -Throw
        }
    }
}

Describe 'ConvertTo-FloRecruitError' {
    BeforeAll {
        InModuleScope PSFloRecruit {
            # Function is private, so we need to test it in module scope
        }
    }

    It 'Should create error record for 401' {
        InModuleScope PSFloRecruit {
            $exception = New-Object System.Exception "Unauthorized"
            $errorRecord = ConvertTo-FloRecruitError -Exception $exception -StatusCode 401

            $errorRecord | Should -Not -BeNullOrEmpty
            $errorRecord.CategoryInfo.Category | Should -Be 'AuthenticationError'
        }
    }

    It 'Should create error record for 429' {
        InModuleScope PSFloRecruit {
            $exception = New-Object System.Exception "Rate limit exceeded"
            $errorRecord = ConvertTo-FloRecruitError -Exception $exception -StatusCode 429

            $errorRecord | Should -Not -BeNullOrEmpty
            $errorRecord.CategoryInfo.Category | Should -Be 'LimitsExceeded'
        }
    }
}

AfterAll {
    Remove-Module PSFloRecruit -Force -ErrorAction SilentlyContinue
}