@@ -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
|
||||
Reference in New Issue
Block a user