Files
claude-mode/tests/windows/run.ps1
T
smoidoandClaude Opus 5 51e42823d1 Tests, licence, and the tooling to keep the repo consistent
A test suite with no dependencies beyond bash and the Python standard
library, run by one command:

  scripts/test.sh                    (or: make test)
  scripts/test.sh --windows [host]   adds the PowerShell suite over SSH

- tests/static.sh: script syntax, JSON validity, VERSION against the widget
  manifest and CHANGELOG, providers.json against the kinds, probes and checks
  the code implements (and ids that would shadow a command), qmllint and
  shellcheck when installed.
- tests/python: unit tests for cm-json.py - providers and the column contract
  bash depends on, the original three scaffolds byte for byte, every
  catalogue parser and the cache, session scan/dismiss/repair, rename,
  defaults, health, the cost guard.
- tests/cli: the CLI in a sandbox with its own HOME, CM_ROOT,
  CLAUDE_CONFIG_DIR and a file-only vault, against fake Ollama, LM Studio,
  keyed-gateway and proxy servers - preflight, presets, models, doctor, a real
  switch, sessions, and the key helper under a rename race.
- tests/windows: the same idea on a Windows host, with USERPROFILE pointed at
  a temp folder so the real install is never touched.

Also: MIT licence, CHANGELOG.md reconstructed from history, .editorconfig and
.gitattributes pinning LF everywhere (what the tree already is), and
scripts/bump-version.sh, since the version lives in two files and drifted
once before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 01:42:38 +03:00

128 lines
5.5 KiB
PowerShell

# 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", "$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"
$cm = Join-Path $root 'claude-mode.ps1'
$tokens = $null; $errors = $null
[void][System.Management.Automation.Language.Parser]::ParseFile($cm, [ref]$tokens, [ref]$errors)
Expect-Eq $errors.Count 0 'claude-mode.ps1 parses'
foreach ($e in $errors) { Write-Host " | 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