tests/PowerCraft.Secrets.Tests.ps1

#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' }

BeforeAll {
    # Use a temp directory as POWERCRAFT_HOME to avoid touching real secrets
    $script:TestHome = Join-Path ([System.IO.Path]::GetTempPath()) "PCSecretsTest_$([guid]::NewGuid().ToString('N').Substring(0,8))"
    $env:POWERCRAFT_HOME = $script:TestHome

    # Import module
    $modulePath = Join-Path $PSScriptRoot '..' 'PowerCraft.Secrets.psd1'
    Import-Module $modulePath -Force
}

AfterAll {
    # Cleanup
    Remove-Module PowerCraft.Secrets -Force -ErrorAction SilentlyContinue
    if (Test-Path $script:TestHome) {
        Remove-Item $script:TestHome -Recurse -Force
    }
    Remove-Item env:POWERCRAFT_HOME -ErrorAction SilentlyContinue
}

Describe 'Initialize-PCSecretStore' {
    It 'Creates directory and secrets file' {
        $result = Initialize-PCSecretStore
        $result | Should -Be (Join-Path $script:TestHome 'secrets.json')
        Test-Path $result | Should -BeTrue
    }

    It 'Returns path without error on repeated calls' {
        $result = Initialize-PCSecretStore
        $result | Should -Be (Join-Path $script:TestHome 'secrets.json')
    }

    It 'Creates valid empty JSON' {
        $content = Get-Content (Join-Path $script:TestHome 'secrets.json') -Raw
        { $content | ConvertFrom-Json } | Should -Not -Throw
    }

    It '-Force resets to empty' {
        # Add a secret first
        Set-PCSecret -Name 'test-svc' -ApiKey 'key123'
        Test-PCSecret -Name 'test-svc' | Should -BeTrue

        # Force reinitialize
        Initialize-PCSecretStore -Force
        Test-PCSecret -Name 'test-svc' | Should -BeFalse
    }
}

Describe 'Set-PCSecret' {
    BeforeEach {
        Initialize-PCSecretStore -Force
    }

    It 'Stores a secret with -ApiKey' {
        Set-PCSecret -Name 'openai' -ApiKey 'sk-test-12345'
        $result = Get-PCSecret -Name 'openai'
        $result | Should -Be 'sk-test-12345'
    }

    It 'Stores with -Properties hashtable' {
        Set-PCSecret -Name 'azure' -Properties @{ speechKey = 'abc123'; region = 'westeurope' }
        $result = Get-PCSecret -Name 'azure' -Property 'speechKey'
        $result | Should -Be 'abc123'

        $result2 = Get-PCSecret -Name 'azure' -Property 'region'
        $result2 | Should -Be 'westeurope'
    }

    It 'Stores custom envVar mapping' {
        Set-PCSecret -Name 'custom' -ApiKey 'cust-key' -EnvVar 'MY_CUSTOM_VAR'
        $entry = Get-PCSecret -Name 'custom' -AsHashtable
        $entry['envVar'] | Should -Be 'MY_CUSTOM_VAR'
    }

    It 'Overwrites existing secret' {
        Set-PCSecret -Name 'openai' -ApiKey 'first'
        Set-PCSecret -Name 'openai' -ApiKey 'second'
        Get-PCSecret -Name 'openai' | Should -Be 'second'
    }

    It 'Preserves other services when updating one' {
        Set-PCSecret -Name 'svc1' -ApiKey 'key1'
        Set-PCSecret -Name 'svc2' -ApiKey 'key2'
        Get-PCSecret -Name 'svc1' | Should -Be 'key1'
        Get-PCSecret -Name 'svc2' | Should -Be 'key2'
    }
}

Describe 'Get-PCSecret' {
    BeforeAll {
        Initialize-PCSecretStore -Force
        Set-PCSecret -Name 'openai' -ApiKey 'sk-stored-key'
        Set-PCSecret -Name 'multi' -Properties @{ apiKey = 'mk'; extra = 'ev' }
    }

    It 'Returns apiKey by default' {
        Get-PCSecret -Name 'openai' | Should -Be 'sk-stored-key'
    }

    It 'Returns specific property' {
        Get-PCSecret -Name 'multi' -Property 'extra' | Should -Be 'ev'
    }

    It 'Returns $null for non-existent secret' {
        Get-PCSecret -Name 'nonexistent' | Should -BeNullOrEmpty
    }

    It '-Required throws for missing secret' {
        { Get-PCSecret -Name 'nonexistent' -Required } | Should -Throw '*Required secret not found*'
    }

    It '-AsHashtable returns full entry' {
        $result = Get-PCSecret -Name 'multi' -AsHashtable
        $result | Should -BeOfType [hashtable]
        $result['apiKey'] | Should -Be 'mk'
        $result['extra'] | Should -Be 'ev'
    }

    It 'Environment variable takes priority' {
        $env:OPENAI_API_KEY = 'env-override'
        try {
            Get-PCSecret -Name 'openai' | Should -Be 'env-override'
        }
        finally {
            Remove-Item env:OPENAI_API_KEY -ErrorAction SilentlyContinue
        }
    }
}

Describe 'Remove-PCSecret' {
    BeforeAll {
        Initialize-PCSecretStore -Force
        Set-PCSecret -Name 'to-remove' -ApiKey 'val'
        Set-PCSecret -Name 'to-keep' -ApiKey 'keep-val'
    }

    It 'Removes a secret' {
        Remove-PCSecret -Name 'to-remove'
        Test-PCSecret -Name 'to-remove' | Should -BeFalse
    }

    It 'Does not affect other secrets' {
        Get-PCSecret -Name 'to-keep' | Should -Be 'keep-val'
    }

    It 'Warns for non-existent secret' {
        Remove-PCSecret -Name 'ghost' -WarningVariable w 3>$null
        $w | Should -BeLike '*not found*'
    }
}

Describe 'Test-PCSecret' {
    BeforeAll {
        Initialize-PCSecretStore -Force
        Set-PCSecret -Name 'present' -ApiKey 'yes'
    }

    It 'Returns $true for existing secret' {
        Test-PCSecret -Name 'present' | Should -BeTrue
    }

    It 'Returns $false for missing secret' {
        Test-PCSecret -Name 'absent' | Should -BeFalse
    }

    It 'Returns $true when env var is set' {
        $env:MISSING_SVC_API_KEY = 'from-env'
        try {
            Test-PCSecret -Name 'missing-svc' | Should -BeTrue
        }
        finally {
            Remove-Item env:MISSING_SVC_API_KEY -ErrorAction SilentlyContinue
        }
    }
}

Describe 'Get-PCSecretNames' {
    BeforeAll {
        Initialize-PCSecretStore -Force
        Set-PCSecret -Name 'alpha' -ApiKey 'a'
        Set-PCSecret -Name 'beta' -ApiKey 'b'
        Set-PCSecret -Name 'gamma' -ApiKey 'g'
    }

    It 'Returns all service names' {
        $names = Get-PCSecretNames
        $names | Should -Contain 'alpha'
        $names | Should -Contain 'beta'
        $names | Should -Contain 'gamma'
    }

    It 'Returns sorted list' {
        $names = Get-PCSecretNames
        $names[0] | Should -Be 'alpha'
        $names[1] | Should -Be 'beta'
        $names[2] | Should -Be 'gamma'
    }

    It 'Returns empty array for empty store' {
        Initialize-PCSecretStore -Force
        $names = Get-PCSecretNames
        $names.Count | Should -Be 0
    }
}

Describe 'Import-PCSecrets' {
    BeforeAll {
        Initialize-PCSecretStore -Force
        Set-PCSecret -Name 'openai' -ApiKey 'sk-import-test'
        Set-PCSecret -Name 'brave-search' -ApiKey 'bsa-import-test'
        Set-PCSecret -Name 'custom-mapped' -ApiKey 'custom-val' -EnvVar 'MY_MAPPED_KEY'
    }

    AfterEach {
        # Clean up env vars
        Remove-Item env:OPENAI_API_KEY -ErrorAction SilentlyContinue
        Remove-Item env:BRAVE_SEARCH_API_KEY -ErrorAction SilentlyContinue
        Remove-Item env:MY_MAPPED_KEY -ErrorAction SilentlyContinue
    }

    It 'Sets environment variables for all secrets' {
        Import-PCSecrets
        $env:OPENAI_API_KEY | Should -Be 'sk-import-test'
        $env:BRAVE_SEARCH_API_KEY | Should -Be 'bsa-import-test'
    }

    It 'Respects custom envVar mapping' {
        Import-PCSecrets
        $env:MY_MAPPED_KEY | Should -Be 'custom-val'
    }

    It '-Name imports only specified services' {
        Import-PCSecrets -Name 'openai'
        $env:OPENAI_API_KEY | Should -Be 'sk-import-test'
        $env:BRAVE_SEARCH_API_KEY | Should -BeNullOrEmpty
    }

    It '-PassThru returns summary objects' {
        $results = Import-PCSecrets -PassThru
        $results | Should -Not -BeNullOrEmpty
        $results[0] | Should -BeOfType [PSCustomObject]
        ($results | Where-Object Name -eq 'openai').Status | Should -Be 'Imported'
    }

    It 'Reports NotFound for missing service' {
        $results = Import-PCSecrets -Name 'nonexistent' -PassThru -WarningAction SilentlyContinue
        ($results | Where-Object Name -eq 'nonexistent').Status | Should -Be 'NotFound'
    }
}

Describe 'Environment variable name resolution' {
    BeforeAll {
        Initialize-PCSecretStore -Force
    }

    It 'Uses default mapping for known services' {
        Set-PCSecret -Name 'openai' -ApiKey 'x'
        Import-PCSecrets -Name 'openai'
        $env:OPENAI_API_KEY | Should -Be 'x'
        Remove-Item env:OPENAI_API_KEY -ErrorAction SilentlyContinue
    }

    It 'Generates name for unknown services (uppercase + _API_KEY)' {
        Set-PCSecret -Name 'my-new-svc' -ApiKey 'y'
        Import-PCSecrets -Name 'my-new-svc'
        $env:MY_NEW_SVC_API_KEY | Should -Be 'y'
        Remove-Item env:MY_NEW_SVC_API_KEY -ErrorAction SilentlyContinue
    }

    It 'Custom envVar overrides default and generated names' {
        Set-PCSecret -Name 'openai' -ApiKey 'z' -EnvVar 'CUSTOM_OVERRIDE'
        Import-PCSecrets -Name 'openai'
        $env:CUSTOM_OVERRIDE | Should -Be 'z'
        $env:OPENAI_API_KEY | Should -BeNullOrEmpty
        Remove-Item env:CUSTOM_OVERRIDE -ErrorAction SilentlyContinue
    }
}

Describe 'Cross-platform file permissions' {
    It 'Secrets file is created without error' {
        Initialize-PCSecretStore -Force
        $path = Join-Path $script:TestHome 'secrets.json'
        Test-Path $path | Should -BeTrue
    }
}