# lite 版冒烟:起一个隔离实例,把所有页面都渲染一遍(用 curl.exe,绕开受约束语言模式) $ErrorActionPreference = 'Stop' $root = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } if (-not (Test-Path (Join-Path $root 'go.mod'))) { $root = Join-Path $root 'subtrack' } $port = 17779 $exe = Join-Path $root 'daili-lite-smoke.exe' $db = Join-Path $root '.gotmp\smoke-lite.db' $jar = Join-Path $root '.gotmp\cookies.txt' $base = "http://127.0.0.1:$port" $resp = @{} $i = 0 $fail = 0 New-Item -ItemType Directory -Force -Path (Join-Path $root '.gotmp') | Out-Null foreach ($f in @($db, $jar)) { if (Test-Path $f) { Remove-Item $f -Force } } $env:GOFLAGS='-mod=mod'; $env:GOPROXY='off'; $env:GOSUMDB='off' $env:GOCACHE=(Join-Path $root '.gocache'); $env:GOTMPDIR=(Join-Path $root '.gotmp') Push-Location $root go build -o $exe . $buildOk = ($LASTEXITCODE -eq 0) Pop-Location if (-not $buildOk) { Write-Output 'BUILD FAILED'; exit 1 } Write-Output 'build ok' $logFile = Join-Path $root '.gotmp\smoke-server.log' $proc = Start-Process -FilePath $exe -ArgumentList @('-addr', "127.0.0.1:$port", '-db', $db, '-no-open', '-no-backup') -PassThru -RedirectStandardOutput $logFile -RedirectStandardError ($logFile + '.err') -WindowStyle Hidden Start-Sleep -Seconds 2 function Req($name, $path, $method = 'GET', $data = $null) { $body = Join-Path $root ('.gotmp\resp-{0}.html' -f $global:i) $global:i = $global:i + 1 $a = @('-s', '-o', $body, '-w', '%{http_code}', '-b', $global:jar, '-c', $global:jar, '-L', '--max-time', '20') if ($method -eq 'POST') { # 不要用 -X POST:curl 会在 303 重定向后继续 POST,导致下一跳 405 foreach ($k in $data.Keys) { $a = $a + @('--data-urlencode', ($k + '=' + $data[$k])) } } $a = $a + @($global:base + $path) $code = (& curl.exe @a) -join '' $text = '' if (Test-Path $body) { $text = (Get-Content -Raw -Encoding utf8 $body) } if ($null -eq $text) { $text = '' } $bad = $text -match '页面渲染失败|模板不存在' $status = if ($bad) { 'TEMPLATE-ERROR' } else { 'ok' } if ($bad -or $code -ne '200') { $global:fail = $global:fail + 1 } Write-Output ('{0,-26} {1} {2} len={3}' -f $name, $code, $status, $text.Length) $global:resp[$name] = $text } function Post($name, $path, $data) { Req $name $path 'POST' $data } Write-Output '--- 页面渲染 ---' Req 'GET /login' '/login' Req 'GET /setup' '/setup' Post 'POST /setup' '/setup' @{ username = 'admin'; display_name = 'boss'; password = 'daili123'; password2 = 'daili123' } Req 'GET /calendar' '/calendar' Req 'GET /settings' '/settings' Req 'GET /logs' '/logs' Req 'GET /packages' '/packages' Req 'GET /packages/new' '/packages/new' Post 'POST /packages' '/packages' @{ name = 'monthly'; default_amount = '30'; price = '300'; color = '#3B6EA5'; sort_order = '0'; note = 'smoke' } Req 'GET /packages/1' '/packages/1' Req 'GET /packages/1/edit' '/packages/1/edit' Req 'GET /customers' '/customers' Req 'GET /customers/new' '/customers/new' Post 'POST /customers' '/customers' @{ tail = '1234'; name = 'laozhang'; game_name = 'wzry'; game_server = 'wx'; contact = 'wx'; source = 'douyin'; tags = 'vip'; note = 'smoke' } Req 'GET /customers/1' '/customers/1' Req 'GET /customers/1/edit' '/customers/1/edit' Post 'POST /sub-create' '/customers/1/subscriptions' @{ package_id = '1'; start_date = '2026-01-01'; days = '30'; end_date = ''; note = 'smoke' } Req 'GET /subscriptions' '/subscriptions' Req 'GET /calendar?extend' '/calendar?extend=1' Req 'GET /calendar?pick' '/calendar?pick=1234' Post 'POST /checkin' '/subscriptions/1/checkin' @{ date = '2026-01-01'; status = 'done'; note = '' } Post 'POST /settings' '/settings' @{ warn_days = '5' } Post 'POST /password' '/settings/password' @{ old_password = 'daili123'; password = 'daili456'; password2 = 'daili456' } Write-Output '--- 真实数据(今天开通 → 日历应有待打任务)---' $today = (Get-Date).ToString('yyyy-MM-dd') Post 'POST /packages2' '/packages' @{ name = 'weekly'; default_amount = '7'; price = '80'; color = '#884422'; sort_order = '0' } Post 'POST /sub2' '/customers/1/subscriptions' @{ package_id = '2'; start_date = $today; days = '30'; end_date = '' } Req 'GET /calendar-live' '/calendar' $live = $resp['GET /calendar-live'] Write-Output '--- 已移除的路由 ---' foreach ($gone in @('/screen', '/customer', '/api/screen/day')) { $code = (& curl.exe -s -o NUL -w '%{http_code}' -b $jar --max-time 10 ($base + $gone)) -join '' $tag = if ($code -eq '404') { 'ok (404)' } else { 'UNEXPECTED'; } if ($code -ne '404') { $fail = $fail + 1 } Write-Output ('{0,-26} {1} {2}' -f ('GONE ' + $gone), $code, $tag) } Write-Output '--- 内容断言 ---' $cal = $resp['GET /calendar'] function Assert($label, $haystack, $pattern, $expectFound) { $found = $haystack -match $pattern $pass = ($found -eq $expectFound) if (-not $pass) { $global:fail = $global:fail + 1 } $verdict = if ($pass) { 'ok' } else { 'FAIL' } $what = if ($expectFound) { 'present' } else { 'absent' } Write-Output ('{0,-34} {1} ({2} expected)' -f $label, $verdict, $what) } Assert '盲水印 Powered by Baka春秋' $cal 'Powered by Baka春秋' $true Assert 'wm-blind 容器' $cal 'class="wm-blind"' $true Assert '品牌 代打台-L' $cal '代打台-L' $true Assert '页脚版本 v1.11.5-L' $cal 'v1\.11\.5-L' $true Assert '大屏入口' $cal '大屏' $false Assert '/screen 链接' $cal '/screen' $false Assert '通行证' $cal '通行证' $false Assert '深渊' $cal '深渊' $false Assert '按次' $cal '按次' $false Assert '小键盘' $cal '小键盘' $false Assert '打手' $cal '打手' $false Assert 'keypad 标记' $cal 'data-keypad' $false Write-Output '--- 实时数据断言 ---' Assert '日历有待打任务' $live '今日打卡|当日待完成' $true Assert '剩余 30 天' $live '剩 30 天' $true Assert '客户尾号 1234' $live '1234' $true Assert '今天到期徽标' $live '今日到期' $true Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue Write-Output '--- server log ---' if (Test-Path $logFile) { Get-Content $logFile -Encoding utf8 | Select-Object -First 30 } Write-Output ('SMOKE-FAILURES=' + $fail)