首次推送
build / build-and-test (push) Failing after 1m24s

This commit is contained in:
2026-10-18 19:44:38 +08:00
commit f047662129
64 changed files with 10454 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
# 端到端冒烟测试:真实 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
}