Public/Tests/Protect-ConfigValue.Tests.ps1

BeforeAll {
    $env:TCS_CONFIG_ROOT = Join-Path -Path $TestDrive -ChildPath 'config'
    $env:TCS_SKIP_UPDATE_CHECK = '1'
    $env:TCS_TELEMETRY_OPTOUT = '1'
    $ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
    Import-Module -Name (Join-Path -Path $ModuleRoot -ChildPath 'tcs.core.psd1') -Force
}

AfterAll {
    Remove-Module -Name tcs.core -Force -ErrorAction SilentlyContinue
}

Describe 'Protect-ConfigValue' {
    It "Should return an encrypted string" {
        $plaintext = "MySecretValue"
        $encrypted = Protect-ConfigValue -Value $plaintext
        $encrypted | Should -Not -Be $plaintext
    }

    It "Should return a non-empty string" {
        $encrypted = Protect-ConfigValue -Value "TestValue"
        $encrypted | Should -Not -BeNullOrEmpty
    }
}

BeforeDiscovery {
    # -Skip conditions are evaluated during discovery, before any BeforeAll block runs
    $onWindows = $PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows
    $isRoot = (-not $onWindows) -and ((& id -u) -eq '0')
}

Describe 'Protect-ConfigValue format and platforms' {
    BeforeAll {
        $key = New-Object byte[] 32
        [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($key)
    }

    It 'Produces a versioned, self-describing value' {
        Protect-ConfigValue -Value 'abc' | Should -Match '^tcs:v1:(dpapi-cu|aes-cu):[A-Za-z0-9+/=]+$'
    }

    It 'Never contains the plaintext, in any encoding' {
        $protected = Protect-ConfigValue -Value 'PlainTextMarker'
        $protected | Should -Not -Match 'PlainTextMarker'
        $hexUtf16 = -join ([System.Text.Encoding]::Unicode.GetBytes('PlainTextMarker') | ForEach-Object { $_.ToString('x2') })
        $protected | Should -Not -Match $hexUtf16
    }

    It 'Produces a different value each time (random IV)' {
        Protect-ConfigValue -Value 'same' | Should -Not -Be (Protect-ConfigValue -Value 'same')
    }

    It 'Uses DPAPI on Windows' -Skip:(-not $onWindows) {
        Protect-ConfigValue -Value 'x' -Scope LocalMachine | Should -Match '^tcs:v1:dpapi-lm:'
    }

    It 'Creates the user key with owner-only permissions on Linux/macOS' -Skip:$onWindows {
        $null = Protect-ConfigValue -Value 'x'
        $keyPath = Join-Path $env:TCS_CONFIG_ROOT 'tcs.core/protection.key'
        Test-Path $keyPath | Should -BeTrue
        (& stat -c '%a' $keyPath 2>$null) + (& stat -f '%Lp' $keyPath 2>$null) | Should -Match '600'
    }

    It 'Fails clearly for LocalMachine scope when the machine key cannot be created' -Skip:($onWindows -or $isRoot) {
        $env:TCS_MACHINE_KEY_PATH = '/proc/tcs-not-writable/protection.key'
        try {
            { Protect-ConfigValue -Value 'x' -Scope LocalMachine } | Should -Throw '*LocalMachine*'
        }
        finally {
            $env:TCS_MACHINE_KEY_PATH = $null
        }
    }

    It 'Encrypts with a supplied key' {
        Protect-ConfigValue -Value 'x' -Key $key | Should -Match '^tcs:v1:aes-key:'
    }

    It 'Rejects keys that are not 32 bytes' {
        { Protect-ConfigValue -Value 'x' -Key (New-Object byte[] 16) } | Should -Throw
    }
}