mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
323 lines
11 KiB
PowerShell
323 lines
11 KiB
PowerShell
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$root = Split-Path -Parent $MyInvocation.MyCommand.Path | Split-Path -Parent
|
|
Set-Location $root
|
|
|
|
Write-Host "[tests] repo: $root"
|
|
|
|
# Cache isolation for pre-commit + black
|
|
$env:PRE_COMMIT_HOME = if ($env:PRE_COMMIT_HOME) { $env:PRE_COMMIT_HOME } else { "$root\.tmp\pre-commit-win" }
|
|
$env:BLACK_CACHE_DIR = if ($env:BLACK_CACHE_DIR) { $env:BLACK_CACHE_DIR } else { "$root\.tmp\black-cache" }
|
|
New-Item -ItemType Directory -Force $env:PRE_COMMIT_HOME | Out-Null
|
|
New-Item -ItemType Directory -Force $env:BLACK_CACHE_DIR | Out-Null
|
|
|
|
function Require-Cmd($cmd) {
|
|
if (-not (Get-Command $cmd -ErrorAction SilentlyContinue)) {
|
|
throw "[tests] ERROR: missing command: $cmd"
|
|
}
|
|
}
|
|
|
|
function Invoke-Checked {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$Label,
|
|
[Parameter(Mandatory = $true)][scriptblock]$Command
|
|
)
|
|
& $Command
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "[tests] ERROR: $Label failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
function Get-GitDiffSnapshot {
|
|
param([switch]$Cached)
|
|
# IMPORTANT: compare both worktree and index; pre-commit can mutate staged files while exiting 0.
|
|
if ($Cached) {
|
|
return (& git diff --cached --binary -- . | Out-String)
|
|
}
|
|
return (& git diff --binary -- . | Out-String)
|
|
}
|
|
|
|
function Assert-PreCommitDidNotMutateRepo {
|
|
param(
|
|
[Parameter(Mandatory = $true)][string]$BeforeWorktree,
|
|
[Parameter(Mandatory = $true)][string]$BeforeIndex
|
|
)
|
|
|
|
$afterWorktree = Get-GitDiffSnapshot
|
|
$afterIndex = Get-GitDiffSnapshot -Cached
|
|
if ($BeforeWorktree -ne $afterWorktree -or $BeforeIndex -ne $afterIndex) {
|
|
throw "[tests] ERROR: pre-commit hooks modified tracked files (worktree or index). Review/stage the hook changes, then rerun the acceptance gate.`n$(& git status --short | Out-String)"
|
|
}
|
|
}
|
|
|
|
Require-Cmd node
|
|
Require-Cmd npm
|
|
|
|
function Ensure-NpmDeps {
|
|
$playwrightPkg = Join-Path $root "node_modules\@playwright\test\package.json"
|
|
if (Test-Path $playwrightPkg) {
|
|
return
|
|
}
|
|
|
|
Write-Host "[tests] Installing frontend dependencies via npm install ..."
|
|
Invoke-Checked "npm install" { npm install }
|
|
}
|
|
|
|
# Prefer project-local virtualenv to avoid global PATH / cache conflicts on Windows.
|
|
$venvPython = Join-Path $root ".venv\Scripts\python.exe"
|
|
function New-ProjectVenv {
|
|
Write-Host "[tests] Creating project venv at $root\.venv ..."
|
|
if (Get-Command py -ErrorAction SilentlyContinue) {
|
|
& py -3 -m venv .venv
|
|
}
|
|
elseif (Get-Command python -ErrorAction SilentlyContinue) {
|
|
& python -m venv .venv
|
|
}
|
|
else {
|
|
throw "[tests] ERROR: no bootstrap Python found (need py or python)"
|
|
}
|
|
}
|
|
|
|
function Test-VenvPython {
|
|
param([string]$PythonExe)
|
|
if (-not (Test-Path $PythonExe)) {
|
|
return $false
|
|
}
|
|
try {
|
|
& $PythonExe -c "import sys; print(sys.executable)" | Out-Null
|
|
return $true
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Test-VenvCfgWindowsCompatible {
|
|
$cfg = Join-Path $root ".venv\pyvenv.cfg"
|
|
if (-not (Test-Path $cfg)) {
|
|
return $false
|
|
}
|
|
try {
|
|
$content = Get-Content $cfg -Raw
|
|
# WSL/Linux-built venvs typically contain POSIX home paths (e.g. /usr/bin)
|
|
if ($content -match "home\s*=\s*/") {
|
|
return $false
|
|
}
|
|
return $true
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path $venvPython)) {
|
|
New-ProjectVenv
|
|
}
|
|
elseif (-not (Test-VenvCfgWindowsCompatible)) {
|
|
Write-Host "[tests] WARN: existing .venv was created from non-Windows interpreter; recreating ..."
|
|
Remove-Item -Recurse -Force ".venv"
|
|
New-ProjectVenv
|
|
}
|
|
elseif (-not (Test-VenvPython -PythonExe $venvPython)) {
|
|
Write-Host "[tests] WARN: existing .venv is invalid for current OS/interpreter; recreating ..."
|
|
Remove-Item -Recurse -Force ".venv"
|
|
New-ProjectVenv
|
|
}
|
|
|
|
if (-not (Test-VenvPython -PythonExe $venvPython)) {
|
|
throw "[tests] ERROR: project venv python is not runnable: $venvPython"
|
|
}
|
|
|
|
$hasPreCommit = $true
|
|
& $venvPython -m pre_commit --version | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
$hasPreCommit = $false
|
|
}
|
|
if (-not $hasPreCommit) {
|
|
Write-Host "[tests] Installing pre-commit into project venv ..."
|
|
Invoke-Checked "pip install pre-commit" { & $venvPython -m pip install -U pip pre-commit }
|
|
}
|
|
|
|
$hasAiohttp = $true
|
|
& $venvPython -c "import aiohttp" | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
$hasAiohttp = $false
|
|
}
|
|
if (-not $hasAiohttp) {
|
|
Write-Host "[tests] Installing aiohttp into project venv ..."
|
|
Invoke-Checked "pip install aiohttp" { & $venvPython -m pip install aiohttp }
|
|
}
|
|
|
|
$hasCrypto = $true
|
|
& $venvPython -c "import Cryptodome" | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
$hasCrypto = $false
|
|
}
|
|
if (-not $hasCrypto) {
|
|
Write-Host "[tests] Installing pycryptodomex into project venv (R82 AES) ..."
|
|
Invoke-Checked "pip install pycryptodomex" { & $venvPython -m pip install pycryptodomex }
|
|
}
|
|
|
|
$hasCryptography = $true
|
|
& $venvPython -c "import cryptography" | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
$hasCryptography = $false
|
|
}
|
|
if (-not $hasCryptography) {
|
|
Write-Host "[tests] Installing cryptography into project venv (S57 Fernet AEAD) ..."
|
|
Invoke-Checked "pip install cryptography" { & $venvPython -m pip install cryptography }
|
|
}
|
|
|
|
$hasDefusedXml = $true
|
|
& $venvPython -c "import defusedxml" | Out-Null
|
|
if ($LASTEXITCODE -ne 0) {
|
|
$hasDefusedXml = $false
|
|
}
|
|
if (-not $hasDefusedXml) {
|
|
# IMPORTANT: keep Windows full-test bootstrap aligned with requirements.txt.
|
|
Write-Host "[tests] Installing defusedxml into project venv (S85 fail-closed XML parsing) ..."
|
|
Invoke-Checked "pip install defusedxml" { & $venvPython -m pip install defusedxml }
|
|
}
|
|
|
|
# Ensure Node >= 18
|
|
$nodeMajor = [int]((& node -p "process.versions.node.split('.')[0]").Trim())
|
|
if ($nodeMajor -lt 18) {
|
|
Write-Host "[tests] WARN: Node < 18 detected. Trying nvm use 18..."
|
|
if (Get-Command nvm -ErrorAction SilentlyContinue) {
|
|
nvm use 18 | Out-Null
|
|
$nodeMajor = [int]((& node -p "process.versions.node.split('.')[0]").Trim())
|
|
}
|
|
}
|
|
if ($nodeMajor -lt 18) {
|
|
throw "[tests] ERROR: Node >=18 required, current=$(node -v)"
|
|
}
|
|
|
|
function Test-PortBindable {
|
|
param([Parameter(Mandatory = $true)][int]$Port)
|
|
|
|
$listener = $null
|
|
try {
|
|
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Parse("127.0.0.1"), $Port)
|
|
$listener.Server.ExclusiveAddressUse = $true
|
|
$listener.Start()
|
|
return $true
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
finally {
|
|
if ($listener -ne $null) {
|
|
$listener.Stop()
|
|
}
|
|
}
|
|
}
|
|
|
|
function Resolve-E2EPort {
|
|
$requested = $env:OPENCLAW_E2E_PORT
|
|
if ($requested) {
|
|
[int]$requestedPort = 0
|
|
if (-not [int]::TryParse($requested, [ref]$requestedPort)) {
|
|
throw "[tests] ERROR: OPENCLAW_E2E_PORT must be an integer, got '$requested'"
|
|
}
|
|
if (-not (Test-PortBindable -Port $requestedPort)) {
|
|
throw "[tests] ERROR: OPENCLAW_E2E_PORT=$requestedPort is not bindable on 127.0.0.1"
|
|
}
|
|
return $requestedPort
|
|
}
|
|
|
|
foreach ($candidate in @(3000, 3300, 3400, 3500, 3600)) {
|
|
if (Test-PortBindable -Port $candidate) {
|
|
return $candidate
|
|
}
|
|
}
|
|
|
|
throw "[tests] ERROR: no bindable local port found for Playwright webServer (tried 3000,3300,3400,3500,3600)"
|
|
}
|
|
|
|
$selectedE2EPort = Resolve-E2EPort
|
|
$env:OPENCLAW_E2E_PORT = "$selectedE2EPort"
|
|
if ($selectedE2EPort -ne 3000) {
|
|
Write-Host "[tests] WARN: port 3000 unavailable; using OPENCLAW_E2E_PORT=$selectedE2EPort for Playwright."
|
|
}
|
|
else {
|
|
Write-Host "[tests] INFO: using OPENCLAW_E2E_PORT=$selectedE2EPort for Playwright."
|
|
}
|
|
|
|
Write-Host "[tests] Node version: $(node -v)"
|
|
Ensure-NpmDeps
|
|
|
|
Write-Host "[tests] 0/8 R120 dependency preflight"
|
|
Invoke-Checked "preflight_check" { & $venvPython scripts\preflight_check.py --strict }
|
|
|
|
Write-Host "[tests] 1/8 detect-secrets"
|
|
Invoke-Checked "detect-secrets" { & $venvPython -m pre_commit run detect-secrets --all-files }
|
|
|
|
Write-Host "[tests] 2/8 pre-commit all hooks (pass 1: autofix)"
|
|
$preCommitWorktreeBefore = Get-GitDiffSnapshot
|
|
$preCommitIndexBefore = Get-GitDiffSnapshot -Cached
|
|
& $venvPython -m pre_commit run --all-files --show-diff-on-failure
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host "[tests] INFO: pre-commit reported changes/issues; running pass 2 verification..."
|
|
Invoke-Checked "pre-commit all hooks (pass 2 verify)" { & $venvPython -m pre_commit run --all-files --show-diff-on-failure }
|
|
}
|
|
Assert-PreCommitDidNotMutateRepo -BeforeWorktree $preCommitWorktreeBefore -BeforeIndex $preCommitIndexBefore
|
|
|
|
Write-Host "[tests] 3/9 coverage governance check"
|
|
Invoke-Checked "coverage governance check" {
|
|
& $venvPython scripts\verify_quality_governance.py
|
|
}
|
|
|
|
Write-Host "[tests] 4/9 backend unit tests"
|
|
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_unit"
|
|
Invoke-Checked "backend unit tests" {
|
|
& $venvPython scripts\run_unittests.py --start-dir tests --pattern "test_*.py" --enforce-skip-policy tests\skip_policy.json
|
|
}
|
|
|
|
if ($env:OPENCLAW_IMPL_RECORD_PATH) {
|
|
Write-Host "[tests] 4.5/9 implementation record lint (strict)"
|
|
# IMPORTANT: strict mode is opt-in via OPENCLAW_IMPL_RECORD_PATH to avoid retroactive legacy record failures.
|
|
Invoke-Checked "implementation record lint" {
|
|
& $venvPython scripts\lint_implementation_record.py --path $env:OPENCLAW_IMPL_RECORD_PATH --strict
|
|
}
|
|
}
|
|
|
|
Write-Host "[tests] 5/9 backend real E2E lanes (R122/R123)"
|
|
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_backend_e2e_real"
|
|
Invoke-Checked "backend real E2E lane R122" {
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r122_real_backend_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
}
|
|
Invoke-Checked "backend real E2E lane R123" {
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r123_real_backend_model_list_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
}
|
|
|
|
Write-Host "[tests] 6/9 R121 retry partition contract"
|
|
Invoke-Checked "R121 retry partition contract" {
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r121_retry_partition_contract --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
}
|
|
|
|
Write-Host "[tests] 7/9 Slack integration gates (R124/R125/R117/F57)"
|
|
Invoke-Checked "Slack integration gates" {
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r124_slack_ingress_contract --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r125_slack_real_backend_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r117_observability_redaction_e2e --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
& $venvPython scripts\run_unittests.py --module tests.test_r117_observability_redaction_endpoints --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
& $venvPython scripts\run_unittests.py --module tests.test_f57_slack_transport_parity --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
& $venvPython scripts\run_unittests.py --module tests.test_f57_slack_socket_mode_startup --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
|
}
|
|
|
|
Write-Host "[tests] 8/9 R118 adversarial gate (adaptive: smoke/extended)"
|
|
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_adversarial"
|
|
Invoke-Checked "R118 adversarial adaptive" {
|
|
& $venvPython scripts\run_adversarial_gate.py --profile auto --seed 42 --artifact-dir .tmp\adversarial
|
|
}
|
|
|
|
Write-Host "[tests] 9/9 frontend E2E"
|
|
$env:OPENCLAW_PLAYWRIGHT_INSTALL = "1"
|
|
$env:OPENCLAW_PLAYWRIGHT_BROWSERS = "chromium"
|
|
# IMPORTANT: full-gate acceptance must provision Playwright browsers itself; do
|
|
# not assume a warmed local browser cache when running on fresh Windows hosts.
|
|
Invoke-Checked "frontend E2E" { npm test }
|
|
|
|
Write-Host "[tests] PASS"
|