Files
ccs/tests/native/windows/custom-claude-path.ps1
T
kaitranntt 66e93fcae1 refactor(tests): Phase 3 - Move native tests to new structure
- Copy Unix and Windows tests to native/ directories
- Update Unix tests to source shared helpers
- Remove Section 10 (npm tests) from native tests
- Update final results to use shared helper functions
2025-11-05 11:16:16 -05:00

875 lines
29 KiB
PowerShell

# CCS Custom Claude CLI Path - Comprehensive Test Suite
# Tests CCS_CLAUDE_PATH environment variable support (v2.3.0)
# Windows PowerShell 5.1+ compatible
param(
[switch]$Verbose,
[switch]$QuickTest # Skip slow tests
)
$ErrorActionPreference = "Stop"
# --- Test Framework ---
$Script:TotalTests = 0
$Script:PassedTests = 0
$Script:FailedTests = 0
$Script:SkippedTests = 0
$Script:StartTime = Get-Date
$Script:TestResults = @()
function Write-TestHeader {
param([string]$Category)
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " $Category" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
}
function Write-TestResult {
param(
[string]$TestName,
[string]$Status, # PASS, FAIL, SKIP
[string]$Details = "",
[int]$DurationMs = 0
)
$Script:TotalTests++
$Symbol = switch ($Status) {
"PASS" { "[OK]"; $Script:PassedTests++; $Color = "Green" }
"FAIL" { "[X]"; $Script:FailedTests++; $Color = "Red" }
"SKIP" { "[i]"; $Script:SkippedTests++; $Color = "Yellow" }
}
Write-Host "$Symbol $TestName" -ForegroundColor $Color
if ($Details) {
Write-Host " $Details" -ForegroundColor Gray
}
if ($DurationMs -gt 0) {
Write-Host " Duration: ${DurationMs}ms" -ForegroundColor Gray
}
$Script:TestResults += [PSCustomObject]@{
TestName = $TestName
Status = $Status
Details = $Details
DurationMs = $DurationMs
}
}
# --- Test Environment Setup ---
$Script:TestDir = "$env:TEMP\ccs-test-$(Get-Random)"
$Script:MockClaudeDir = Join-Path $TestDir "mock-claude"
$Script:OriginalEnv = @{
CCS_CLAUDE_PATH = $env:CCS_CLAUDE_PATH
PATH = $env:PATH
}
function Initialize-TestEnvironment {
Write-Host "`n[Initializing Test Environment]" -ForegroundColor Cyan
Write-Host " Test Directory: $Script:TestDir"
# Create test directories
New-Item -ItemType Directory -Path $Script:TestDir -Force | Out-Null
New-Item -ItemType Directory -Path $Script:MockClaudeDir -Force | Out-Null
# Create mock claude.exe (simple executable that returns version)
$MockExePath = Join-Path $Script:MockClaudeDir "claude.exe"
# PowerShell script wrapped as executable
$MockScript = @'
# Mock Claude CLI
Write-Host "Mock Claude CLI v1.0.0"
exit 0
'@
# Create a batch file that calls PowerShell (simplest executable)
$BatchContent = @"
@echo off
echo Mock Claude CLI v1.0.0
exit /b 0
"@
Set-Content -Path $MockExePath -Value $BatchContent -Force
Write-Host " [OK] Created mock claude.exe at $MockExePath" -ForegroundColor Green
Write-Host ""
}
function Restore-TestEnvironment {
Write-Host "`n[Cleaning Up Test Environment]" -ForegroundColor Cyan
# Restore original environment variables
$env:CCS_CLAUDE_PATH = $Script:OriginalEnv.CCS_CLAUDE_PATH
$env:PATH = $Script:OriginalEnv.PATH
# Remove test directory
if (Test-Path $Script:TestDir) {
Remove-Item -Path $Script:TestDir -Recurse -Force -ErrorAction SilentlyContinue
Write-Host " [OK] Removed test directory" -ForegroundColor Green
}
Write-Host ""
}
# --- Helper Functions ---
function Get-CcsPath {
# Try to find CCS in multiple locations
$PossiblePaths = @(
"$env:USERPROFILE\.ccs\ccs.ps1", # Installed location
(Join-Path (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) "ccs.ps1"), # Repo location
(Get-Command ccs -ErrorAction SilentlyContinue).Source # From PATH
)
foreach ($Path in $PossiblePaths) {
if ($Path -and (Test-Path $Path)) {
return $Path
}
}
throw "CCS script not found. Checked: $($PossiblePaths -join ', ')"
}
function Test-ClaudeDetection {
param(
[string]$ExpectedPath = "",
[bool]$ShouldSucceed = $true
)
$CcsPath = Get-CcsPath
try {
# Extract Find-ClaudeCli function and test it
$CcsContent = Get-Content $CcsPath -Raw
# Execute detection logic in isolated scope
$DetectionScript = {
param($CcsContent, $TestEnv)
# Set up test environment
foreach ($key in $TestEnv.Keys) {
Set-Item -Path "env:$key" -Value $TestEnv[$key]
}
# Extract and execute Find-ClaudeCli function
$FunctionStart = $CcsContent.IndexOf("function Find-ClaudeCli {")
$FunctionEnd = $CcsContent.IndexOf("`n}", $FunctionStart) + 2
$Function = $CcsContent.Substring($FunctionStart, $FunctionEnd - $FunctionStart)
Invoke-Expression $Function
return Find-ClaudeCli
}
$TestEnv = @{
CCS_CLAUDE_PATH = $env:CCS_CLAUDE_PATH
PATH = $env:PATH
}
$Result = & $DetectionScript -CcsContent $CcsContent -TestEnv $TestEnv
if ($ShouldSucceed) {
if ([string]::IsNullOrEmpty($Result)) {
throw "Detection failed: No path returned"
}
if ($ExpectedPath -and ($Result -ne $ExpectedPath)) {
throw "Detection returned wrong path: $Result (expected: $ExpectedPath)"
}
return $Result
} else {
if (-not [string]::IsNullOrEmpty($Result)) {
throw "Detection should have failed but returned: $Result"
}
return ""
}
} catch {
if ($ShouldSucceed) {
throw $_
}
return ""
}
}
function Get-MockClaudePath {
return Join-Path $Script:MockClaudeDir "claude.exe"
}
# --- Test Category 1: Environment Variable Detection (Priority 1) ---
function Test-Category1-EnvVarDetection {
Write-TestHeader "Category 1: Environment Variable Detection (Priority 1)"
# Test 1.1: Valid CCS_CLAUDE_PATH
$TestStart = Get-Date
try {
$MockPath = Get-MockClaudePath
$env:CCS_CLAUDE_PATH = $MockPath
$DetectedPath = Test-ClaudeDetection -ExpectedPath $MockPath -ShouldSucceed $true
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.1: Valid CCS_CLAUDE_PATH" `
-Status "PASS" `
-Details "Detected: $DetectedPath" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.1: Valid CCS_CLAUDE_PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 1.2: Invalid CCS_CLAUDE_PATH (non-existent file)
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = "D:\nonexistent\claude.exe"
# Should fall back to PATH or common locations
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.2: Invalid CCS_CLAUDE_PATH (non-existent)" `
-Status "PASS" `
-Details "Correctly fell back to search" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.2: Invalid CCS_CLAUDE_PATH (non-existent)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 1.3: CCS_CLAUDE_PATH is a directory (not file)
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $Script:TestDir
# Should fail validation and fall back
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.3: CCS_CLAUDE_PATH is directory" `
-Status "PASS" `
-Details "Validation correctly rejected directory" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.3: CCS_CLAUDE_PATH is directory" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 1.4: CCS_CLAUDE_PATH with special characters (spaces)
$TestStart = Get-Date
try {
# Create mock in path with spaces
$SpacePath = Join-Path $Script:TestDir "Program Files (x86)"
New-Item -ItemType Directory -Path $SpacePath -Force | Out-Null
$SpaceClaudePath = Join-Path $SpacePath "claude.exe"
Copy-Item (Get-MockClaudePath) $SpaceClaudePath -Force
$env:CCS_CLAUDE_PATH = $SpaceClaudePath
$DetectedPath = Test-ClaudeDetection -ExpectedPath $SpaceClaudePath -ShouldSucceed $true
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.4: CCS_CLAUDE_PATH with spaces" `
-Status "PASS" `
-Details "Handled spaces correctly" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 1.4: CCS_CLAUDE_PATH with spaces" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
}
# --- Test Category 2: PATH Detection (Priority 2) ---
function Test-Category2-PathDetection {
Write-TestHeader "Category 2: PATH Detection (Priority 2)"
# Test 2.1: Claude in PATH
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$MockPath = Get-MockClaudePath
$env:PATH = "$Script:MockClaudeDir;$env:PATH"
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $true
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 2.1: Claude in PATH" `
-Status "PASS" `
-Details "Found via PATH: $DetectedPath" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 2.1: Claude in PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:PATH = $Script:OriginalEnv.PATH
}
# Test 2.2: No CCS_CLAUDE_PATH, no PATH
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
# Keep original PATH (no mock claude in it)
# Should fall back to common locations (will fail in test environment)
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 2.2: No CCS_CLAUDE_PATH, no PATH" `
-Status "PASS" `
-Details "Correctly fell back to Priority 3" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 2.2: No CCS_CLAUDE_PATH, no PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
}
# --- Test Category 3: Common Locations (Priority 3) ---
function Test-Category3-CommonLocations {
Write-TestHeader "Category 3: Common Locations (Priority 3)"
# Test 3.1: Claude in C:\Program Files
$TestStart = Get-Date
try {
if ($QuickTest) {
Write-TestResult -TestName "Test 3.1: Claude in C:\Program Files" `
-Status "SKIP" `
-Details "Skipped in quick test mode"
} else {
# This test requires admin rights to create in C:\Program Files
Write-TestResult -TestName "Test 3.1: Claude in C:\Program Files" `
-Status "SKIP" `
-Details "Requires admin rights to test"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 3.1: Claude in C:\Program Files" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
# Test 3.2: Claude on D drive
$TestStart = Get-Date
try {
if ($QuickTest) {
Write-TestResult -TestName "Test 3.2: Claude on D drive" `
-Status "SKIP" `
-Details "Skipped in quick test mode"
} else {
# This test requires D: drive to exist
if (Test-Path "D:\") {
Write-TestResult -TestName "Test 3.2: Claude on D drive" `
-Status "SKIP" `
-Details "Requires D: drive setup"
} else {
Write-TestResult -TestName "Test 3.2: Claude on D drive" `
-Status "SKIP" `
-Details "D: drive not available"
}
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 3.2: Claude on D drive" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
# Test 3.3: Claude not found anywhere
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$env:PATH = $Script:OriginalEnv.PATH
# Remove mock from PATH, should fail to find anywhere
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 3.3: Claude not found anywhere" `
-Status "PASS" `
-Details "Correctly returned empty when not found" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 3.3: Claude not found anywhere" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
}
# --- Test Category 4: Security Validation ---
function Test-Category4-SecurityValidation {
Write-TestHeader "Category 4: Security Validation"
# Test 4.1: Command injection attempt (semicolon)
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = "claude.exe; rm -rf /"
# Should be rejected by character validation
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.1: Injection attempt (semicolon)" `
-Status "PASS" `
-Details "Blocked semicolon character" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.1: Injection attempt (semicolon)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 4.2: Command injection attempt (pipe)
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = "claude.exe | malicious.exe"
# Should be rejected by character validation
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.2: Injection attempt (pipe)" `
-Status "PASS" `
-Details "Blocked pipe character" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.2: Injection attempt (pipe)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 4.3: Command injection attempt (backtick)
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = "claude.exe`nmalicious.exe"
# Should be rejected by character validation
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.3: Injection attempt (backtick)" `
-Status "PASS" `
-Details "Blocked backtick/newline" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.3: Injection attempt (backtick)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 4.4: Path traversal attempt
$TestStart = Get-Date
try {
# Relative paths should be allowed (valid use case)
$RelativePath = "..\..\Windows\System32\cmd.exe"
$env:CCS_CLAUDE_PATH = $RelativePath
# Should resolve to absolute path and validate
# Will fail on non-executable, but path format is OK
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.4: Path traversal (relative paths OK)" `
-Status "PASS" `
-Details "Relative paths allowed, validation on file type" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 4.4: Path traversal (relative paths OK)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
}
# --- Test Category 5: Error Messages ---
function Test-Category5-ErrorMessages {
Write-TestHeader "Category 5: Error Messages"
# Test 5.1: Error message completeness
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$env:PATH = $Script:OriginalEnv.PATH
# Capture error output
$CcsPath = Get-CcsPath
$ErrorOutput = & $CcsPath --help 2>&1 | Out-String
# Check for required sections in error message
$HasCcsClaudePathStatus = $ErrorOutput -match "CCS_CLAUDE_PATH:"
$HasPathSearch = $ErrorOutput -match "System PATH:"
$HasCommonLocations = $ErrorOutput -match "Common locations:"
$HasSolutions = $ErrorOutput -match "Solutions:"
$HasDebugging = $ErrorOutput -match "Debugging:"
$AllSectionsPresent = $HasCcsClaudePathStatus -and $HasPathSearch -and
$HasCommonLocations -and $HasSolutions -and $HasDebugging
if ($AllSectionsPresent) {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 5.1: Error message completeness" `
-Status "PASS" `
-Details "All required sections present" `
-DurationMs $Duration
} else {
throw "Missing sections in error message"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 5.1: Error message completeness" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
# Test 5.2: Error message D drive examples
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$env:PATH = $Script:OriginalEnv.PATH
$CcsPath = Get-CcsPath
$ErrorOutput = & $CcsPath --help 2>&1 | Out-String
$HasDDriveExample = $ErrorOutput -match "D:"
if ($HasDDriveExample) {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 5.2: Error message D drive examples" `
-Status "PASS" `
-Details "D: drive examples present" `
-DurationMs $Duration
} else {
throw "D: drive examples missing"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 5.2: Error message D drive examples" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
}
# --- Test Category 6: Integration Tests ---
function Test-Category6-Integration {
Write-TestHeader "Category 6: Integration Tests"
# Test 6.1: Full workflow with CCS_CLAUDE_PATH
$TestStart = Get-Date
try {
$MockPath = Get-MockClaudePath
$env:CCS_CLAUDE_PATH = $MockPath
$CcsPath = Get-CcsPath
# Run ccs with mock claude (will fail on config but detection should work)
$Output = & $CcsPath --help 2>&1 | Out-String
# Check if it used the custom Claude path (not error about not finding)
$UsedCustomPath = -not ($Output -match "Claude CLI not found")
if ($UsedCustomPath) {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.1: Full workflow with CCS_CLAUDE_PATH" `
-Status "PASS" `
-Details "Used custom Claude path successfully" `
-DurationMs $Duration
} else {
throw "Did not use custom Claude path"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.1: Full workflow with CCS_CLAUDE_PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 6.2: Version command bypasses detection
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$CcsPath = Get-CcsPath
$Output = & $CcsPath --version 2>&1 | Out-String
$ShowsVersion = $Output -match "CCS \(Claude Code Switch\) version"
if ($ShowsVersion) {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.2: Version command bypasses detection" `
-Status "PASS" `
-Details "Version shown without Claude detection" `
-DurationMs $Duration
} else {
throw "Version command failed"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.2: Version command bypasses detection" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
# Test 6.3: Help command uses detection
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = $null
$CcsPath = Get-CcsPath
$Output = & $CcsPath --help 2>&1 | Out-String
$TriedDetection = $Output -match "Claude CLI not found"
if ($TriedDetection) {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.3: Help command uses detection" `
-Status "PASS" `
-Details "Help command triggered Claude detection" `
-DurationMs $Duration
} else {
throw "Help command did not trigger detection"
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 6.3: Help command uses detection" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
}
}
# --- Test Category 7: Edge Cases ---
function Test-Category7-EdgeCases {
Write-TestHeader "Category 7: Edge Cases"
# Test 7.1: Empty CCS_CLAUDE_PATH
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = ""
# Should treat as unset and continue fallback
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.1: Empty CCS_CLAUDE_PATH" `
-Status "PASS" `
-Details "Treated as unset, continued fallback" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.1: Empty CCS_CLAUDE_PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 7.2: Whitespace-only CCS_CLAUDE_PATH
$TestStart = Get-Date
try {
$env:CCS_CLAUDE_PATH = " "
# Should fail validation and continue fallback
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.2: Whitespace-only CCS_CLAUDE_PATH" `
-Status "PASS" `
-Details "Validation failed, continued fallback" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.2: Whitespace-only CCS_CLAUDE_PATH" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 7.3: Very long path (>260 characters)
$TestStart = Get-Date
try {
if ($QuickTest) {
Write-TestResult -TestName "Test 7.3: Very long path (>260 chars)" `
-Status "SKIP" `
-Details "Skipped in quick test mode"
} else {
# Create a path longer than 260 characters
$LongPath = "C:\" + ("very-long-directory-name\" * 20) + "claude.exe"
$env:CCS_CLAUDE_PATH = $LongPath
# Should handle long paths (may fail on file not found, not path length)
$DetectedPath = Test-ClaudeDetection -ShouldSucceed $false
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.3: Very long path (>260 chars)" `
-Status "PASS" `
-Details "Handled long path without crash" `
-DurationMs $Duration
}
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.3: Very long path (>260 chars)" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
# Test 7.4: Unicode in path
$TestStart = Get-Date
try {
# Create directory with Unicode characters
$UnicodePath = Join-Path $Script:TestDir "文件夹"
New-Item -ItemType Directory -Path $UnicodePath -Force | Out-Null
$UnicodeClaudePath = Join-Path $UnicodePath "claude.exe"
Copy-Item (Get-MockClaudePath) $UnicodeClaudePath -Force
$env:CCS_CLAUDE_PATH = $UnicodeClaudePath
$DetectedPath = Test-ClaudeDetection -ExpectedPath $UnicodeClaudePath -ShouldSucceed $true
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.4: Unicode in path" `
-Status "PASS" `
-Details "Handled Unicode characters correctly" `
-DurationMs $Duration
} catch {
$Duration = ((Get-Date) - $TestStart).TotalMilliseconds
Write-TestResult -TestName "Test 7.4: Unicode in path" `
-Status "FAIL" `
-Details $_.Exception.Message `
-DurationMs $Duration
} finally {
$env:CCS_CLAUDE_PATH = $null
}
}
# --- Main Execution ---
function Show-TestSummary {
$EndTime = Get-Date
$TotalDuration = ($EndTime - $Script:StartTime).TotalSeconds
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " TEST SUMMARY" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
Write-Host "Total Tests: $Script:TotalTests"
Write-Host "Passed: $Script:PassedTests" -ForegroundColor Green
Write-Host "Failed: $Script:FailedTests" -ForegroundColor Red
Write-Host "Skipped: $Script:SkippedTests" -ForegroundColor Yellow
$PassRate = if ($Script:TotalTests -gt 0) {
[math]::Round(($Script:PassedTests / $Script:TotalTests) * 100, 2)
} else {
0
}
Write-Host "Pass Rate: $PassRate%"
Write-Host "Duration: ${TotalDuration}s"
Write-Host "`n"
# Show failures
if ($Script:FailedTests -gt 0) {
Write-Host "FAILED TESTS:" -ForegroundColor Red
$Script:TestResults | Where-Object { $_.Status -eq "FAIL" } | ForEach-Object {
Write-Host " - $($_.TestName)" -ForegroundColor Red
Write-Host " $($_.Details)" -ForegroundColor Gray
}
Write-Host "`n"
}
# Exit code
if ($Script:FailedTests -gt 0) {
exit 1
} else {
exit 0
}
}
# --- Run Tests ---
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " CCS Custom Claude CLI Path Test Suite" -ForegroundColor Cyan
Write-Host " Version: 2.3.0" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan
Initialize-TestEnvironment
try {
Test-Category1-EnvVarDetection
Test-Category2-PathDetection
Test-Category3-CommonLocations
Test-Category4-SecurityValidation
Test-Category5-ErrorMessages
Test-Category6-Integration
Test-Category7-EdgeCases
Show-TestSummary
} finally {
Restore-TestEnvironment
}