Files
chun_qiu f047662129
build / build-and-test (push) Failing after 1m24s
首次推送
2026-10-18 19:44:38 +08:00

216 lines
9.3 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 端到端冒烟测试:真实 HTTP 走一遍主要流程
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\smoke.ps1 -Base http://127.0.0.1:18080
param(
[string]$Base = 'http://127.0.0.1:18080',
[string]$User = 'admin',
[string]$Pass = 'daili123'
)
$ErrorActionPreference = 'Stop'
$script:fail = 0
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
function Send {
param([string]$Method, [string]$Path, $Body)
$p = @{
Uri = "$Base$Path"
WebSession = $session
UseBasicParsing = $true
MaximumRedirection = 5
Method = $Method
}
if ($null -ne $Body) { $p.Body = $Body }
try {
return Invoke-WebRequest @p
} catch [System.Net.WebException] {
$resp = $_.Exception.Response
if ($null -eq $resp) { throw }
$sr = New-Object System.IO.StreamReader($resp.GetResponseStream())
$body = $sr.ReadToEnd()
$sr.Close()
return [pscustomobject]@{
StatusCode = [int]$resp.StatusCode
Content = $body
BaseResponse = [pscustomobject]@{ ResponseUri = $resp.ResponseUri }
}
}
}
function Check {
param([bool]$Cond, [string]$Msg)
if ($Cond) {
Write-Host (" [ok] " + $Msg) -ForegroundColor Green
} else {
Write-Host (" [FAIL] " + $Msg) -ForegroundColor Red
$script:fail++
}
}
function Final-Path($resp) {
if ($resp.PSObject.Properties.Name -contains 'BaseResponse' -and $resp.BaseResponse -and $resp.BaseResponse.ResponseUri) {
return $resp.BaseResponse.ResponseUri.AbsolutePath
}
return ''
}
$now = Get-Date
$today = $now.ToString('yyyy-MM-dd')
$tail = Get-Random -Minimum 1000 -Maximum 9999
Write-Host "`n=== 代打台冒烟测试 @ $Base ($today / 尾号 $tail) ===" -ForegroundColor Cyan
# 1. 初始化管理员(或登录)
$r = Send 'GET' '/setup'
if ($r.Content -match '创建第一个管理员') {
$r = Send 'POST' '/setup' @{ username = $User; display_name = '老板'; password = $Pass; password2 = $Pass }
Check ($r.StatusCode -eq 200) '创建管理员并自动登录'
Check ((Final-Path $r) -like '/packages*') '初始化后跳转到套餐页'
} else {
$r = Send 'POST' '/login' @{ username = $User; password = $Pass; next = '/calendar' }
Check ($r.StatusCode -eq 200 -and (Final-Path $r) -like '/calendar*') '已存在管理员,登录成功'
}
# 2. 套餐
$r = Send 'GET' '/packages'
Check ($r.StatusCode -eq 200) '套餐页可访问'
$colors = @('#2F5D50', '#6B4E9B')
$pkgs = @(
@{ name = '包月日常'; unit = 'day'; default_amount = 30; price = 300; note = '每天日常 + 周常' },
@{ name = '深渊十次'; unit = 'count'; default_amount = 10; price = 180; note = '按次结算' }
)
for ($i = 0; $i -lt $pkgs.Count; $i++) {
$p = $pkgs[$i]
$r = Send 'POST' '/packages' @{
name = $p.name; unit = $p.unit; default_amount = "$($p.default_amount)"
price = "$($p.price)"; color = $colors[$i]; note = $p.note; sort_order = "$($i + 1)"
}
Check ($r.Content -match '套餐已保存|套餐已创建|套餐') "提交套餐 $($p.name)"
}
$r = Send 'GET' '/packages'
Check ($r.Content -match '包月日常' -and $r.Content -match '深渊十次') '套餐列表包含两个套餐'
$r = Send 'GET' '/packages/1/edit'
Check ($r.StatusCode -eq 200 -and $r.Content -match '包月日常') '套餐编辑页可打开'
# 3. 客户
$r = Send 'POST' '/customers' @{
tail = "$tail"; name = '老张'; game_name = '王者荣耀'; game_server = '微信区'
contact = 'wx: laozhang'; tags = '老客户,包月'; source = '抖音'; note = '喜欢晚上打'
}
$custPath = Final-Path $r
Check ($custPath -like '/customers/*') "新建客户成功 -> $custPath"
$custID = ($custPath -split '/')[-1]
$r = Send 'GET' "/customers?q=$tail"
Check ($r.Content -match '老张') "尾号 $tail 搜索命中老张"
$r = Send 'POST' '/customers' @{ tail = "$tail"; name = '老张' }
Check ($r.StatusCode -eq 400 -and $r.Content -match '已存在') '重复客户被拒绝并提示'
# 4. 订阅(按日 + 按次)
$r = Send 'POST' "/customers/$custID/subscriptions" @{ package_id = '1'; start_date = $today; days = '30'; note = '首单' }
Check ($r.Content -match '已开通') '开通按日订阅'
$r = Send 'POST' "/customers/$custID/subscriptions" @{ package_id = '1'; start_date = $today; days = '30' }
Check ($r.Content -match '追加时长') '同一套餐重复订阅被拦截'
$r = Send 'POST' "/customers/$custID/subscriptions" @{
package_id = '2'; start_date = $today; total_count = '10'; end_date = $now.AddDays(10).ToString('yyyy-MM-dd')
}
Check ($r.Content -match '已开通') '开通按次订阅'
$r = Send 'GET' "/customers/$custID"
$subIDs = [regex]::Matches($r.Content, 'id="sub-(\d+)"') | ForEach-Object { [int]$_.Groups[1].Value }
Check ($subIDs.Count -ge 2) "客户页出现 2 张订阅卡(实际 $($subIDs.Count))"
$daySub = $subIDs[0]
$cntSub = $subIDs[1]
# 5. 追加时长(日历手动选终止日:今天 +45 天)
$newEnd = $now.AddDays(45)
$oldEnd = $now.AddDays(29)
$expectDays = ($newEnd.Date - $oldEnd.Date).Days
$r = Send 'POST' "/subscriptions/$daySub/extend" @{
new_end_date = $newEnd.ToString('yyyy-MM-dd'); note = '微信转账续费'; back = "/customers/$custID"
}
Check ($r.Content -match "已追加 $expectDays 天") "追加时长反算正确(期望 $expectDays 天)"
$r = Send 'GET' "/customers/$custID"
Check ($r.Content -match '剩 46 天') '客户页剩余天数更新为 46 天'
Check ($r.Content -match '微信转账续费') '追加记录已留痕(备注)'
Check ($r.Content -match '追加天数') '追加记录标注类型'
# 6. 日历
$r = Send 'GET' "/calendar?y=$($now.Year)&m=$($now.Month)&d=$today"
Check ($r.StatusCode -eq 200) '日历页可打开'
Check ($r.Content -match '老张') '日历当天出现该客户色签'
Check ($r.Content -match '当日待打') '日历抽屉存在'
# 7. 打卡(按日)
$r = Send 'POST' "/subscriptions/$daySub/checkin" @{ date = $today; status = 'done'; note = '打完'; back = "/calendar?d=$today" }
Check ($r.Content -match '已记录') '按日打卡成功'
# 8. 按次打卡扣次
$r = Send 'POST' "/subscriptions/$cntSub/checkin" @{ date = $today; status = 'done'; back = "/customers/$custID" }
Check ($r.Content -match '已记录') '按次打卡成功(应扣 1 次)'
$r = Send 'GET' "/customers/$custID"
Check ($r.Content -match '剩 9 / 10 次') '按次剩余次数扣减为 9'
# 9. 暂停 / 恢复
$r = Send 'POST' "/subscriptions/$daySub/pause" @{ back = "/customers/$custID" }
Check ($r.Content -match '已暂停') '暂停订阅'
$r = Send 'GET' "/customers/$custID"
Check ($r.Content -match '暂停中') '客户页显示暂停中'
$r = Send 'POST' "/subscriptions/$daySub/resume" @{ back = "/customers/$custID" }
Check ($r.Content -match '已恢复') '恢复订阅'
# 9.5 无有效期的按次订阅:靠客户页「今日打卡」扣次
$r = Send 'POST' '/packages' @{
name = '副本五次'; unit = 'count'; default_amount = '5'; price = '120'; color = '#41556B'; note = '无有效期'; sort_order = '3'
}
Check ($r.Content -match '套餐') '新建第三个套餐(按次、无有效期)'
$r = Send 'GET' '/packages'
$pkgIDs = [regex]::Matches($r.Content, '/packages/(\d+)/edit') | ForEach-Object { [int]$_.Groups[1].Value }
$newPkg = ($pkgIDs | Measure-Object -Maximum).Maximum
$r = Send 'POST' "/customers/$custID/subscriptions" @{ package_id = "$newPkg"; start_date = $today; total_count = '5' }
Check ($r.Content -match '已开通') '开通无有效期的按次订阅'
$r = Send 'GET' "/customers/$custID"
Check ($r.Content -match '今日打卡') '订阅卡片出现「今日打卡」按钮'
$ids3 = [regex]::Matches($r.Content, 'id="sub-(\d+)"') | ForEach-Object { [int]$_.Groups[1].Value }
$thirdSub = $ids3[-1]
$r = Send 'POST' "/subscriptions/$thirdSub/checkin" @{ date = $today; status = 'done'; back = "/customers/$custID" }
Check ($r.Content -match '已记录') '无有效期按次订阅打卡成功'
$r = Send 'GET' "/customers/$custID"
Check ($r.Content -match '剩 4 / 5 次') '打卡后次数扣减为 4'
# 10. 大屏
$r = Send 'GET' '/screen'
Check ($r.StatusCode -eq 200) '大屏可访问'
Check ($r.Content -match '即将到期 TOP 10') '大屏含 TOP 榜'
Check ($r.Content -match '到期热力') '大屏含热力月历'
Check ($r.Content -match "$tail") '大屏显示该客户尾号'
# 11. 日志 / 设置 / 导出
$r = Send 'GET' '/logs'
Check ($r.Content -match '追加时长') '操作日志含追加记录'
$r = Send 'GET' '/settings'
Check ($r.StatusCode -eq 200 -and $r.Content -match '基础设置') '设置页可打开'
$r = Send 'GET' '/export/customers.csv'
Check ($r.StatusCode -eq 200 -and $r.Content -match '手机尾号') '客户 CSV 导出正常'
$r = Send 'GET' '/export/subscriptions.csv'
Check ($r.StatusCode -eq 200 -and $r.Content -match '剩余') '订阅 CSV 导出正常'
$r = Send 'GET' '/export/logs.csv'
Check ($r.StatusCode -eq 200 -and $r.Content -match '操作人') '日志 CSV 导出正常'
# 12. 静态资源与健康检查
$r = Send 'GET' '/static/app.css'
Check ($r.StatusCode -eq 200 -and $r.Content -match 'ticket') '静态样式可访问'
$r = Send 'GET' '/healthz'
Check ($r.Content -match 'ok') '健康检查正常'
Write-Host ""
if ($script:fail -eq 0) {
Write-Host "全部通过" -ForegroundColor Green
exit 0
} else {
Write-Host ("失败 $script:fail 项") -ForegroundColor Red
exit 1
}