Tests/Private/Logging.Tests.ps1

BeforeDiscovery {
    Import-Module (Join-Path $PSScriptRoot '..\..\Use-Onyphe.psd1') -DisableNameChecking -Force
}

Describe 'Private/Logging functions' -Tag 'Unit' {
    InModuleScope 'Use-Onyphe' {

        BeforeEach {
            $script:LoggingSinkFailed = $false
            $script:LoggingConfigWarned = $false
        }

        Context 'Resolve-OnypheLogFilePath' {
            It 'expands environment variables and date tokens, creating the directory' {
                $Dir = Join-Path $TestDrive 'LogsDir'
                $File = [PSCustomObject]@{ Path = $Dir; FileName = 'Use-Onyphe-{yyyyMMdd}.log' }

                $Result = Resolve-OnypheLogFilePath -File $File

                $Result | Should -Be (Join-Path $Dir "Use-Onyphe-$(Get-Date -Format 'yyyyMMdd').log")
                Test-Path $Dir | Should -BeTrue
            }
        }

        Context 'Write-OnypheFileLogEntry' {
            It 'appends a formatted line to the resolved log file' {
                $Dir = Join-Path $TestDrive 'FileLogs'
                $File = [PSCustomObject]@{ Path = $Dir; FileName = 'test.log' }
                $Entry = [PSCustomObject]@{ Timestamp = Get-Date '2026-01-01 10:00:00'; Level = 'Information'; UserName = 'tester'; CmdletName = 'Get-OnypheInfo'; Message = 'hello' }

                Write-OnypheFileLogEntry -File $File -Entry $Entry

                $Content = Get-Content (Join-Path $Dir 'test.log') -Raw
                $Content | Should -Match 'INFORMATION'
                $Content | Should -Match 'tester'
                $Content | Should -Match 'Get-OnypheInfo'
                $Content | Should -Match 'hello'
            }

            It 'never throws and warns once when resolving the log path fails' {
                Mock Resolve-OnypheLogFilePath { throw 'bad path' }
                $Entry = [PSCustomObject]@{ Timestamp = Get-Date; Level = 'Error'; UserName = 'tester'; CmdletName = 'X'; Message = 'boom' }

                { Write-OnypheFileLogEntry -File ([PSCustomObject]@{ Path = 'x'; FileName = 'y' }) -Entry $Entry -WarningAction SilentlyContinue } | Should -Not -Throw
                $script:LoggingSinkFailed | Should -BeTrue
            }
        }

        Context 'Test-OnypheEventSourceExists' {
            It 'returns a boolean' {
                (Test-OnypheEventSourceExists -Source 'Application') | Should -BeOfType [bool]
            }
        }

        Context 'Write-OnypheEventLogEntry' {
            It 'warns once and skips when Write-EventLog is not available on this host' {
                Mock Get-Command { $null } -ParameterFilter { $Name -eq 'Write-EventLog' }

                $EventLog = [PSCustomObject]@{ LogName = 'Application'; Source = 'Use-Onyphe'; EventIdInformation = 1000; EventIdWarning = 2000; EventIdError = 3000 }
                $Entry = [PSCustomObject]@{ Timestamp = Get-Date; Level = 'Information'; UserName = 'tester'; CmdletName = 'X'; Message = 'hi' }

                { Write-OnypheEventLogEntry -EventLog $EventLog -Entry $Entry -WarningAction SilentlyContinue } | Should -Not -Throw
                $script:LoggingSinkFailed | Should -BeTrue
            }
        }

        Context 'Write-OnypheLog' {
            It 'no-ops when logging is disabled (default)' {
                Mock Write-OnypheFileLogEntry { }
                Mock Write-OnypheEventLogEntry { }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $false } }

                Write-OnypheLog -Config $Config -Level Information -CmdletName 'X' -Message 'hi'

                Should -Invoke Write-OnypheFileLogEntry -Times 0
                Should -Invoke Write-OnypheEventLogEntry -Times 0
            }

            It 'no-ops when the entry level is below MinimumLevel' {
                Mock Write-OnypheFileLogEntry { }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $true; MinimumLevel = 'Warning'; Mode = 'File' } }

                Write-OnypheLog -Config $Config -Level Debug -CmdletName 'X' -Message 'hi'

                Should -Invoke Write-OnypheFileLogEntry -Times 0
            }

            It 'dispatches to Write-OnypheFileLogEntry when Mode is File and level meets MinimumLevel' {
                Mock Write-OnypheFileLogEntry { }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $true; MinimumLevel = 'Information'; Mode = 'File' } }

                Write-OnypheLog -Config $Config -Level Information -CmdletName 'X' -Message 'hi'

                Should -Invoke Write-OnypheFileLogEntry -Times 1 -Exactly
            }

            It 'dispatches to Write-OnypheEventLogEntry when Mode is EventLog' {
                Mock Write-OnypheEventLogEntry { }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $true; MinimumLevel = 'Information'; Mode = 'EventLog' } }

                Write-OnypheLog -Config $Config -Level Information -CmdletName 'X' -Message 'hi'

                Should -Invoke Write-OnypheEventLogEntry -Times 1 -Exactly
            }

            It 'redacts Credential/Password/Secret/Token/APIKey-named bound parameters' {
                Mock Write-OnypheFileLogEntry { }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $true; MinimumLevel = 'Debug'; Mode = 'File' } }
                $Bound = @{ APIKey = 'super-secret-key'; SearchValue = '9.9.9.9' }

                Write-OnypheLog -Config $Config -Level Debug -CmdletName 'X' -Message 'hi' -BoundParameters $Bound

                Should -Invoke Write-OnypheFileLogEntry -Times 1 -Exactly -ParameterFilter {
                    ($Entry.Message -notmatch 'super-secret-key') -and ($Entry.Message -match 'APIKey=<redacted>') -and ($Entry.Message -match 'SearchValue=9.9.9.9')
                }
            }

            It 'never throws even when Get-OnypheLoggingConfig fails' {
                Mock Get-OnypheLoggingConfig { throw 'boom' }
                $Config = [PSCustomObject]@{ Logging = [PSCustomObject]@{ Enabled = $true } }

                { Write-OnypheLog -Config $Config -Level Information -CmdletName 'X' -Message 'hi' -WarningAction SilentlyContinue } | Should -Not -Throw
            }
        }
    }
}