首次推送
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
+160
View File
@@ -0,0 +1,160 @@
# 数据可移植性测试:单文件、只拷 daili.db 无损迁移、删库即重置
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\portability-test.ps1
param(
[string]$Exe = 'F:\hupeE的代肝平台\daili.exe',
[string]$Work = 'F:\hupeE的代肝平台\portable-run',
[int]$PortA = 17781,
[int]$PortB = 17782
)
$ErrorActionPreference = 'Continue'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Add-Type -AssemblyName System.Net.Http
$script:fail = 0
$script:results = @()
function Check {
param([string]$Id, [string]$Name, $Cond, [string]$Detail = '')
if ($Cond -eq $true) {
Write-Host (" [PASS] {0} {1}" -f $Id, $Name) -ForegroundColor Green
$script:results += "PASS|$Id|$Name|$Detail"
} else {
Write-Host (" [FAIL] {0} {1} {2}" -f $Id, $Name, $Detail) -ForegroundColor Red
$script:results += "FAIL|$Id|$Name|$Detail"
$script:fail++
}
}
function New-Client {
$h = New-Object System.Net.Http.HttpClientHandler
$h.UseCookies = $true
$h.AllowAutoRedirect = $true
$c = New-Object System.Net.Http.HttpClient($h)
$c.Timeout = [TimeSpan]::FromSeconds(10)
return $c
}
function Post-Form($client, $url, $kv) {
$pairs = New-Object 'System.Collections.Generic.List[System.Collections.Generic.KeyValuePair[string,string]]'
foreach ($k in $kv.Keys) { $pairs.Add([System.Collections.Generic.KeyValuePair[string,string]]::new($k, [string]$kv[$k])) }
return $client.PostAsync($url, [System.Net.Http.FormUrlEncodedContent]::new($pairs)).Result
}
function Get-Page($client, $url) {
try {
$r = $client.GetAsync($url).Result
return @{ Code = [int]$r.StatusCode; Path = $r.RequestMessage.RequestUri.AbsolutePath; Body = $r.Content.ReadAsStringAsync().Result }
} catch { return @{ Code = 0; Path = ''; Body = '' } }
}
function Wait-Up([int]$port, [int]$seconds = 15) {
$c = New-Client
for ($i = 0; $i -lt ($seconds * 4); $i++) {
if ((Get-Page $c "http://127.0.0.1:$port/healthz").Code -eq 200) { return $true }
Start-Sleep -Milliseconds 250
}
return $false
}
function Start-App([string]$dir, [int]$port) {
$p = Start-Process -FilePath (Join-Path $dir 'daili.exe') -ArgumentList @('-addr', "127.0.0.1:$port", '-no-open') `
-WorkingDirectory $dir -WindowStyle Hidden -PassThru `
-RedirectStandardOutput (Join-Path $dir 'stdout.log') -RedirectStandardError (Join-Path $dir 'stderr.log')
return @{ Proc = $p; Up = (Wait-Up $port) }
}
function Stop-App($h) {
if ($h -and $h.Proc) { Stop-Process -Id $h.Proc.Id -Force -ErrorAction SilentlyContinue }
Start-Sleep -Milliseconds 700
}
function New-AppDir([string]$name) {
$dir = Join-Path $Work $name
Remove-Item -Recurse -Force $dir -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $dir | Out-Null
Copy-Item $Exe (Join-Path $dir 'daili.exe') -Force
return $dir
}
Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $Work | Out-Null
$today = (Get-Date).Date.ToString('yyyy-MM-dd')
$newEnd = (Get-Date).Date.AddDays(45).ToString('yyyy-MM-dd')
Write-Host "`n=== 数据可移植性测试 ===" -ForegroundColor Cyan
# ---------- 1. A 目录造数据 ----------
Write-Host "`n-- 1. 在 A 目录造数据 --" -ForegroundColor Cyan
$dirA = New-AppDir 'A'
$hA = Start-App $dirA $PortA
Check 'P-01' 'A 目录启动成功' $hA.Up ""
$cA = New-Client
Post-Form $cA "http://127.0.0.1:$PortA/setup" @{ username = 'admin'; display_name = '老板'; password = 'daili123'; password2 = 'daili123' } | Out-Null
Post-Form $cA "http://127.0.0.1:$PortA/packages" @{ name = '包月日常'; unit = 'day'; default_amount = '30'; price = '300'; color = '#2F5D50'; note = ''; sort_order = '1' } | Out-Null
Post-Form $cA "http://127.0.0.1:$PortA/customers" @{ tail = '8001'; name = '迁移客户'; game_name = '王者荣耀'; game_server = ''; contact = 'wx:8001'; tags = '老客户'; source = ''; note = '迁移前数据' } | Out-Null
Post-Form $cA "http://127.0.0.1:$PortA/customers/1/subscriptions" @{ package_id = '1'; start_date = $today; days = '30'; note = '首单' } | Out-Null
$detail = (Get-Page $cA "http://127.0.0.1:$PortA/customers/1").Body
$subId = [regex]::Match($detail, 'id="sub-(\d+)"').Groups[1].Value
Post-Form $cA "http://127.0.0.1:$PortA/subscriptions/$subId/extend" @{ new_end_date = $newEnd; note = '迁移前续费'; back = '/customers/1' } | Out-Null
Post-Form $cA "http://127.0.0.1:$PortA/subscriptions/$subId/checkin" @{ date = $today; status = 'done'; note = '迁移前打卡'; back = '/customers/1' } | Out-Null
$detail = (Get-Page $cA "http://127.0.0.1:$PortA/customers/1").Body
Check 'P-02' '造数成功(剩 46 天 + 打卡)' ($detail -match '剩 46 天' -and $detail -match '迁移前打卡') ""
Stop-App $hA
# ---------- 2. 关闭后应当只有一个数据库文件 ----------
Write-Host "`n-- 2. 关闭后检查文件形态 --" -ForegroundColor Cyan
$dbA = Join-Path $dirA 'daili.db'
$sidecars = @()
foreach ($s in '-wal', '-shm', '-journal') {
if (Test-Path ($dbA + $s)) { $sidecars += $s }
}
Check 'P-03' '关闭后只有 daili.db(无 -wal/-shm/-journal)' ((Test-Path $dbA) -and $sidecars.Count -eq 0) "sidecars=$($sidecars -join ',')"
$sizeA = (Get-Item $dbA).Length
Check 'P-04' '主库文件包含全部数据(远大于空库 4KB)' ($sizeA -gt 20000) "size=$sizeA"
# ---------- 3. 只拷贝 daili.db 到 B 目录 ----------
Write-Host "`n-- 3. 只拷贝 daili.db 到 B 目录 --" -ForegroundColor Cyan
$dirB = New-AppDir 'B'
Copy-Item $dbA (Join-Path $dirB 'daili.db') -Force
$hB = Start-App $dirB $PortB
Check 'P-05' 'B 目录用拷贝的库启动成功' $hB.Up ""
$cB = New-Client
$login = Post-Form $cB "http://127.0.0.1:$PortB/login" @{ username = 'admin'; password = 'daili123'; next = '/customers' }
$page = (Get-Page $cB "http://127.0.0.1:$PortB/customers").Body
Check 'P-06' '原管理员账号可用(口令随库迁移)' ($page -match '共 1 位' -and $page -match '迁移客户') ""
$detailB = (Get-Page $cB "http://127.0.0.1:$PortB/customers/1").Body
Check 'P-07' '订阅与剩余天数完整' ($detailB -match '剩 46 天') ""
Check 'P-08' '打卡与追加记录完整' ($detailB -match '迁移前打卡' -and $detailB -match '迁移前续费') ""
Check 'P-09' '套餐配置完整' ((Get-Page $cB "http://127.0.0.1:$PortB/packages").Body -match '包月日常') ""
$logs = (Get-Page $cB "http://127.0.0.1:$PortB/logs").Body
Check 'P-10' '操作日志完整' ($logs -match '追加时长' -and $logs -match '打卡') ""
Stop-App $hB
for ($i = 0; $i -lt 10 -and (Test-Path ($dbA + '-wal')); $i++) { Start-Sleep -Milliseconds 200 }
$bSide = @()
foreach ($s in '-wal', '-shm', '-journal') { if (Test-Path ((Join-Path $dirB 'daili.db') + $s)) { $bSide += $s } }
Check 'P-11' 'B 目录运行后仍无旁路文件' ($bSide.Count -eq 0) "sidecars=$($bSide -join ',')"
# ---------- 4. 删除 daili.db = 重置 ----------
Write-Host "`n-- 4. 删除 daili.db 重置数据 --" -ForegroundColor Cyan
foreach ($s in '', '-wal', '-shm', '-journal') { Remove-Item ($dbA + $s) -Force -ErrorAction SilentlyContinue }
$hA2 = Start-App $dirA $PortA
Check 'P-12' '删库后可正常启动' $hA2.Up ""
$cA2 = New-Client
$root = Get-Page $cA2 "http://127.0.0.1:$PortA/"
Check 'P-13' '删库后回到初始化(数据已清空)' ($root.Path -eq '/setup' -and $root.Body -match '创建第一个管理员') "path=$($root.Path)"
Stop-App $hA2
# ---------- 汇总 ----------
$pass = @($script:results | Where-Object { $_ -like 'PASS|*' }).Count
$fail = @($script:results | Where-Object { $_ -like 'FAIL|*' }).Count
Write-Host "`n=== 汇总:通过 $pass 项,失败 $fail 项 ===" -ForegroundColor ($(if ($fail -eq 0) { 'Green' } else { 'Red' }))
New-Item -ItemType Directory -Force -Path 'F:\hupeE的代肝平台\test-evidence' | Out-Null
$script:results | Set-Content 'F:\hupeE的代肝平台\test-evidence\portability-result.txt' -Encoding UTF8
Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue
if ($fail -gt 0) { exit 1 }
exit 0