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 } } |