# Windows suite for claude-mode.ps1. Run on a Windows host by # tests/windows/run-remote.sh, from a copy of the payload in that host's %TEMP%. # # Everything happens in a sandbox: USERPROFILE is pointed at a temp folder for # this process and its children, and the script derives every path from it, so # the host's real ~\.claude-mode, vault and Claude settings are never read or # written. The fake provider servers run on the host's own loopback. # ASCII only: Windows PowerShell 5.1 reads a .ps1 without a BOM as ANSI. $ErrorActionPreference = 'Continue' $src = $PSScriptRoot $sb = Join-Path $env:TEMP ('cm-test-' + [guid]::NewGuid().ToString('N').Substring(0, 8)) $root = Join-Path $sb '.claude-mode' $real = $env:USERPROFILE $script:pass = 0 $script:fail = 0 function Pass { $script:pass++ } function Fail([string] $what, $out) { $script:fail++ Write-Host " FAIL windows: $what" if ($out) { @($out) | Select-Object -Last 8 | ForEach-Object { Write-Host " | $_" } } } function Expect-Match($out, [string] $pattern, [string] $what) { if ((@($out) -join "`n") -match $pattern) { Pass } else { Fail $what $out } } function Expect-Eq($got, $want, [string] $what) { if ("$got" -eq "$want") { Pass } else { Fail "$what (got '$got', wanted '$want')" $null } } New-Item -ItemType Directory -Force -Path "$root\presets", "$root\bin", "$root\lib", "$sb\.claude" | Out-Null Copy-Item "$src\claude-mode.ps1", "$src\providers.json", "$src\VERSION" $root Copy-Item "$src\presets\*.json" "$root\presets" Copy-Item "$src\bin\*" "$root\bin" Copy-Item "$src\lib\*.ps1" "$root\lib" $cm = Join-Path $root 'claude-mode.ps1' # install.ps1 is parsed but never run: it edits the real PowerShell profile and # the User PATH, which no USERPROFILE sandbox contains. $parse = @($cm) + @(Get-ChildItem "$root\lib\*.ps1" | ForEach-Object { $_.FullName }) if (Test-Path "$src\install.ps1") { $parse += "$src\install.ps1" } foreach ($file in $parse) { $tokens = $null; $errors = $null [void][System.Management.Automation.Language.Parser]::ParseFile($file, [ref]$tokens, [ref]$errors) Expect-Eq $errors.Count 0 "$(Split-Path -Leaf $file) parses" foreach ($e in $errors) { Write-Host " | $(Split-Path -Leaf $file) line $($e.Extent.StartLineNumber): $($e.Message)" } } $portsFile = Join-Path $sb 'ports.json' $fake = Start-Process python -ArgumentList ('"' + (Join-Path $src 'fake_server.py') + '"') ` -NoNewWindow -RedirectStandardOutput $portsFile -PassThru for ($i = 0; $i -lt 50; $i++) { if ((Test-Path $portsFile) -and (Get-Item $portsFile).Length -gt 0) { break } Start-Sleep -Milliseconds 100 } $ports = Get-Content $portsFile -TotalCount 1 | ConvertFrom-Json $ollama = "http://127.0.0.1:$($ports.ollama)" $keyed = "http://127.0.0.1:$($ports.keyed)" $env:USERPROFILE = $sb $script:rc = 0 function CM { param([Parameter(ValueFromRemainingArguments = $true)] [string[]] $a) $o = & powershell -NoProfile -ExecutionPolicy Bypass -File $cm @a 2>&1 | ForEach-Object { "$_" } $script:rc = $LASTEXITCODE return $o } function Set-PresetJson([string] $name, [scriptblock] $edit) { $p = Join-Path $root "presets\$name.json" $j = Get-Content $p -Raw | ConvertFrom-Json & $edit $j $j | ConvertTo-Json -Depth 6 | Set-Content $p -Encoding UTF8 } $o = CM help foreach ($id in 'openrouter', 'zai', 'lmstudio', 'ollama', 'custom') { Expect-Match $o "claude-mode $id \[preset\]" "usage lists $id" } $o = CM custom Expect-Eq $rc 1 'custom without an address is refused' Expect-Match $o 'has no server address' 'and says why' Copy-Item "$root\presets\custom.json" "$root\presets\blank.json" Set-PresetJson 'blank' { param($j) $j.baseUrl = 'https://llm.example.com' } $o = CM custom blank Expect-Eq $rc 1 'a preset with no models is refused' Expect-Match $o 'has no models set' 'and says why' Set-PresetJson 'ollama' { param($j) $j.baseUrl = $ollama } $o = CM ollama Expect-Eq $rc 0 'switch to ollama' $envBlock = (Get-Content "$sb\.claude\settings.json" -Raw | ConvertFrom-Json).env Expect-Eq $envBlock.ANTHROPIC_BASE_URL $ollama 'base url written' Expect-Eq $envBlock.ANTHROPIC_AUTH_TOKEN 'ollama' 'placeholder token written' Expect-Eq $envBlock.CLAUDE_CODE_MAX_CONTEXT_TOKENS '65536' 'context declared' $o = CM models Expect-Match $o 'qwen3-coder:latest' 'ollama models listed' $o = CM doctor Expect-Match $o 'Ollama reachable' 'doctor finds the server' Expect-Match $o 'opus\s+qwen3-coder\s+\[30\.5B Q4_K_M\]' 'a bare name matches its :latest tag' Expect-Match $o 'loaded with a 4096-token context' 'the short server context is caught' Expect-Match $o 'OLLAMA_CONTEXT_LENGTH=65536' 'and the fix is named' Set-PresetJson 'custom' { param($j) $j.baseUrl = $keyed $j.auth = [pscustomobject]@{ mode = 'literal'; token = 'sk-real' } $j.models.opus = 'gw/model-a' } $o = CM custom Expect-Eq $rc 0 'switch to a custom endpoint' $o = CM models Expect-Match $o 'gw/model-b' 'a keyed endpoint lists its models' $o = CM 'Z.AI' Expect-Match $o "no key stored for ref 'zai'" 'an alias resolves to its provider' $o = CM nope Expect-Eq $rc 1 'an unknown command fails' $want = '{"provider":"zai","description":"new preset","baseUrl":"https://api.z.ai/api/anthropic","auth":{"mode":"vault","keyRef":"zai"},"models":{"opus":"","sonnet":"","haiku":"","fable":""},"subagentModel":"inherit","gatewayModelDiscovery":false,"contextTokens":1000000,"extraEnv":{"API_TIMEOUT_MS":"3000000","CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC":"1"}}' $got = & powershell -NoProfile -ExecutionPolicy Bypass -Command ". '$cm' help *> `$null; New-PresetScaffold 'zai' | ConvertTo-Json -Depth 5 -Compress" Expect-Eq $got $want 'the zai scaffold is unchanged' Stop-Process -Id $fake.Id -Force -ErrorAction SilentlyContinue $env:USERPROFILE = $real Start-Sleep -Milliseconds 300 Remove-Item -Recurse -Force $sb -ErrorAction SilentlyContinue $line = '{0,-22} {1,3} passed' -f 'windows', $script:pass if ($script:fail -gt 0) { $line += ", $($script:fail) FAILED" } Write-Host $line if ($script:fail -gt 0) { exit 1 } exit 0