Tests/UKG.Tests.ps1

#Requires -Modules Pester

<#
.SYNOPSIS
    Pester unit tests for the UKG PowerShell module.

.DESCRIPTION
    Tests all cmdlets with mocked API responses to verify correct behavior
    without requiring actual API credentials.
#>


BeforeAll {
    # Import the module
    $ModulePath = Split-Path -Parent $PSScriptRoot
    Import-Module $ModulePath -Force

    # Mock response data
    $Script:MockEmployee = @{
        id              = 'emp123'
        email           = 'john.doe@company.com'
        first_name      = 'John'
        last_name       = 'Doe'
        employee_number = 'EMP001'
        status          = 'active'
        organization_id = 'org123'
        created_at      = '2024-01-01T00:00:00Z'
        updated_at      = '2024-01-15T00:00:00Z'
    }

    $Script:MockUser = @{
        id         = 'user123'
        email      = 'admin@company.com'
        first_name = 'Admin'
        last_name  = 'User'
        role_id    = 'role123'
        active     = $true
    }

    $Script:MockOrganization = @{
        id        = 'org123'
        name      = 'Engineering'
        code      = 'ENG'
        parent_id = $null
    }

    $Script:MockRole = @{
        id          = 'role123'
        name        = 'HR Administrator'
        description = 'Full HR access'
    }

    # Helper function to create mock session
    function Get-MockSession {
        return [PSCustomObject]@{
            PSTypeName        = 'UKG.Session'
            ApplicationId     = 'app123'
            ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
            ClientId          = 'client123'
            Region            = 'EU'
            Environment       = 'Production'
            BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
            TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
            AccessToken       = 'mock-access-token'
            TokenType         = 'Bearer'
            TokenExpiry       = (Get-Date).AddHours(1)
            ConnectedAt       = Get-Date
        }
    }
}

Describe 'Module Structure' {
    It 'Should have a module manifest' {
        $ModulePath = Split-Path -Parent $PSScriptRoot
        Test-Path "$ModulePath\PSUKG.psd1" | Should -Be $true
    }

    It 'Should have a root module' {
        $ModulePath = Split-Path -Parent $PSScriptRoot
        Test-Path "$ModulePath\PSUKG.psm1" | Should -Be $true
    }

    It 'Should export expected functions' {
        $Module = Get-Module PSUKG
        $Module.ExportedFunctions.Keys | Should -Contain 'Connect-UKG'
        $Module.ExportedFunctions.Keys | Should -Contain 'Get-UKGEmployee'
        $Module.ExportedFunctions.Keys | Should -Contain 'Get-UKGUser'
    }
}

Describe 'Connect-UKG' {
    BeforeAll {
        Mock Invoke-RestMethod {
            return @{
                access_token = 'mock-token'
                token_type   = 'Bearer'
                expires_in   = 3600
            }
        } -ModuleName PSUKG
    }

    It 'Should connect with valid credentials' {
        $secret = ConvertTo-SecureString 'test-secret' -AsPlainText -Force
        $result = Connect-UKG -ApplicationId 'app123' -ApplicationSecret $secret -ClientId 'client123' -Region EU

        $result | Should -Not -BeNullOrEmpty
        $result.ClientId | Should -Be 'client123'
        $result.Region | Should -Be 'EU'
    }

    It 'Should accept plain text secret' {
        $result = Connect-UKG -ApplicationId 'app123' -ApplicationSecretPlainText 'test-secret' -ClientId 'client123' -Region US

        $result | Should -Not -BeNullOrEmpty
        $result.Region | Should -Be 'US'
    }

    It 'Should use correct base URL for each region' {
        $secret = ConvertTo-SecureString 'test-secret' -AsPlainText -Force

        $result = Connect-UKG -ApplicationId 'app123' -ApplicationSecret $secret -ClientId 'client123' -Region EU -Environment Production
        $result.BaseUrl | Should -Be 'https://apis.eu.people-doc.com/api/v2'

        $result = Connect-UKG -ApplicationId 'app123' -ApplicationSecret $secret -ClientId 'client123' -Region US -Environment Production
        $result.BaseUrl | Should -Be 'https://apis.us.people-doc.com/api/v2'

        $result = Connect-UKG -ApplicationId 'app123' -ApplicationSecret $secret -ClientId 'client123' -Region UKG_ATL -Environment Production
        $result.BaseUrl | Should -Be 'https://apis.hrsd.ultipro.com/api/v2'
    }
}

Describe 'Get-UKGSession' {
    It 'Should return session info when connected' {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                Region            = 'EU'
                Environment       = 'Production'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenType         = 'Bearer'
                TokenExpiry       = (Get-Date).AddHours(1)
                ConnectedAt       = Get-Date
            }
        }

        $result = Get-UKGSession
        $result | Should -Not -BeNullOrEmpty
        $result.ClientId | Should -Be 'client123'
    }

    It 'Should return null when not connected' {
        InModuleScope PSUKG {
            $Script:UKGSession = $null
        }

        $result = Get-UKGSession
        $result | Should -BeNullOrEmpty
    }
}

Describe 'Disconnect-UKG' {
    It 'Should clear the session' {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName  = 'UKG.Session'
                AccessToken = 'mock-token'
            }
        }

        Disconnect-UKG

        InModuleScope PSUKG {
            $Script:UKGSession | Should -BeNullOrEmpty
        }
    }
}

Describe 'Get-UKGEmployee' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockEmp = @{
            id              = 'emp123'
            email           = 'john.doe@company.com'
            first_name      = 'John'
            last_name       = 'Doe'
            employee_number = 'EMP001'
            status          = 'active'
        }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockEmp | ConvertTo-Json)
                Headers    = @{ 'X-Total-Count' = '1' }
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should get employee by ID' {
        $result = Get-UKGEmployee -Id 'emp123'
        $result | Should -Not -BeNullOrEmpty
        $result.id | Should -Be 'emp123'
        $result.email | Should -Be 'john.doe@company.com'
    }

    It 'Should have correct type name' {
        $result = Get-UKGEmployee -Id 'emp123'
        $result.PSObject.TypeNames | Should -Contain 'UKG.Employee'
    }
}

Describe 'New-UKGEmployee' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockEmp = @{
            id         = 'emp123'
            email      = 'john.doe@company.com'
            first_name = 'John'
            last_name  = 'Doe'
        }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockEmp | ConvertTo-Json)
                Headers    = @{}
                StatusCode = 201
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should create employee with required parameters' {
        $result = New-UKGEmployee -Email 'new@company.com' -FirstName 'New' -LastName 'Employee' -Confirm:$false
        $result | Should -Not -BeNullOrEmpty
    }

    It 'Should create employee from InputObject' {
        $empData = @{
            email      = 'input@company.com'
            first_name = 'Input'
            last_name  = 'Object'
        }
        $result = New-UKGEmployee -InputObject $empData -Confirm:$false
        $result | Should -Not -BeNullOrEmpty
    }
}

Describe 'Set-UKGEmployee' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockEmp = @{ id = 'emp123'; email = 'john.doe@company.com' }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockEmp | ConvertTo-Json)
                Headers    = @{}
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should update employee by ID' {
        $result = Set-UKGEmployee -Id 'emp123' -Title 'Senior Developer' -Confirm:$false
        $result | Should -Not -BeNullOrEmpty
    }

    It 'Should update employee by ExternalId' {
        $result = Set-UKGEmployee -ExternalId 'EXT001' -Department 'Engineering' -Confirm:$false
        $result | Should -Not -BeNullOrEmpty
    }
}

Describe 'Search-UKGEmployee' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockEmps = @(@{ id = 'emp123'; email = 'john.doe@company.com' })

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockEmps | ConvertTo-Json)
                Headers    = @{ 'X-Total-Count' = '1'; 'Next-Cursor' = $null }
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should search employees by email' {
        $result = Search-UKGEmployee -Email 'john.doe@company.com'
        $result | Should -Not -BeNullOrEmpty
    }

    It 'Should search employees with filter hashtable' {
        $result = Search-UKGEmployee -Filter @{ status = @{ eq = 'active' } }
        $result | Should -Not -BeNullOrEmpty
    }
}

Describe 'Get-UKGUser' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockUser = @{ id = 'user123'; email = 'admin@company.com' }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockUser | ConvertTo-Json)
                Headers    = @{ 'X-Total-Count' = '1' }
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should get user by ID' {
        $result = Get-UKGUser -Id 'user123'
        $result | Should -Not -BeNullOrEmpty
        $result.id | Should -Be 'user123'
    }

    It 'Should have correct type name' {
        $result = Get-UKGUser -Id 'user123'
        $result.PSObject.TypeNames | Should -Contain 'UKG.User'
    }
}

Describe 'Get-UKGOrganization' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockOrg = @{ id = 'org123'; name = 'Engineering' }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockOrg | ConvertTo-Json)
                Headers    = @{}
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should get organization by ID' {
        $result = Get-UKGOrganization -Id 'org123'
        $result | Should -Not -BeNullOrEmpty
        $result.id | Should -Be 'org123'
        $result.name | Should -Be 'Engineering'
    }
}

Describe 'Get-UKGRole' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockRole = @{ id = 'role123'; name = 'HR Administrator' }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockRole | ConvertTo-Json)
                Headers    = @{}
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should get role by ID' {
        $result = Get-UKGRole -Id 'role123'
        $result | Should -Not -BeNullOrEmpty
        $result.id | Should -Be 'role123'
        $result.name | Should -Be 'HR Administrator'
    }
}

Describe 'Error Handling' {
    It 'Should throw when not connected' {
        InModuleScope PSUKG {
            $Script:UKGSession = $null
        }

        { Get-UKGEmployee -Id 'emp123' } | Should -Throw
    }
}

Describe 'Pagination Helpers' {
    It 'Should parse Next-Cursor header' {
        InModuleScope PSUKG {
            $headers = @{
                'Next-Cursor'   = 'abc123'
                'X-Total-Count' = '100'
                'X-Per-Page'    = '25'
            }

            $result = Get-UKGNextPage -Headers $headers

            $result.NextCursor | Should -Be 'abc123'
            $result.TotalCount | Should -Be 100
            $result.PerPage | Should -Be 25
            $result.HasNextPage | Should -Be $true
        }
    }

    It 'Should handle empty pagination' {
        InModuleScope PSUKG {
            $headers = @{}

            $result = Get-UKGNextPage -Headers $headers

            $result.NextCursor | Should -BeNullOrEmpty
            $result.HasNextPage | Should -Be $false
        }
    }
}

Describe 'ConvertTo-UKGError' {
    It 'Should convert API error response' {
        InModuleScope PSUKG {
            $response = @{
                code    = 'validation_error'
                message = 'Validation failed'
                errors  = @(
                    @{ field = 'email'; message = 'Email is required' }
                )
            }

            $result = ConvertTo-UKGError -Response $response -StatusCode 422

            $result | Should -BeOfType [System.Management.Automation.ErrorRecord]
            $result.CategoryInfo.Category | Should -Be 'InvalidData'
        }
    }

    It 'Should map status codes to error categories' {
        InModuleScope PSUKG {
            $result401 = ConvertTo-UKGError -Response @{} -StatusCode 401
            $result401.CategoryInfo.Category | Should -Be 'AuthenticationError'

            $result403 = ConvertTo-UKGError -Response @{} -StatusCode 403
            $result403.CategoryInfo.Category | Should -Be 'PermissionDenied'

            $result404 = ConvertTo-UKGError -Response @{} -StatusCode 404
            $result404.CategoryInfo.Category | Should -Be 'ObjectNotFound'
        }
    }
}

Describe 'Pipeline Support' {
    BeforeAll {
        InModuleScope PSUKG {
            $Script:UKGSession = [PSCustomObject]@{
                PSTypeName        = 'UKG.Session'
                ApplicationId     = 'app123'
                ApplicationSecret = (ConvertTo-SecureString 'secret' -AsPlainText -Force)
                ClientId          = 'client123'
                BaseUrl           = 'https://apis.eu.people-doc.com/api/v2'
                TokenUrl          = 'https://apis.eu.people-doc.com/oauth/token'
                AccessToken       = 'mock-access-token'
                TokenExpiry       = (Get-Date).AddHours(1)
            }
        }

        $mockEmp = @{ id = 'emp123'; email = 'john.doe@company.com' }

        Mock Invoke-WebRequest {
            $response = [PSCustomObject]@{
                Content    = ($mockEmp | ConvertTo-Json)
                Headers    = @{}
                StatusCode = 200
            }
            return $response
        } -ModuleName PSUKG
    }

    It 'Should accept pipeline input for Set-UKGEmployee' {
        $emp = [PSCustomObject]@{ Id = 'emp123' }
        $result = $emp | Set-UKGEmployee -Title 'Manager' -Confirm:$false
        $result | Should -Not -BeNullOrEmpty
    }
}

AfterAll {
    # Clean up
    Remove-Module PSUKG -Force -ErrorAction SilentlyContinue
}