# 配置文件专项测试:验证 daili.ini 的生成、解析、覆盖与各项生效 # 用法: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\config-test.ps1 param( [string]$Exe = '', [string]$Work = '', [string]$LanIp = '' ) $Root = Split-Path -Parent $PSScriptRoot if (-not $Exe) { $Exe = Join-Path $Root 'daili.exe' } if (-not $Work) { $Work = Join-Path $Root 'config-run' } $Evidence = Join-Path $Root 'test-evidence' if (-not (Test-Path $Exe)) { throw "找不到被测程序:$Exe(先构建或已用 -Exe 指定)" } $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-Status($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++) { $r = Get-Status $c "http://127.0.0.1:$port/healthz" if ($r.Code -eq 200) { return $true } Start-Sleep -Milliseconds 250 } return $false } function Start-App([string]$dir, [string[]]$extra, [int]$port) { $out = Join-Path $dir 'stdout.log' $err = Join-Path $dir 'stderr.log' $p = Start-Process -FilePath (Join-Path $dir 'daili.exe') -ArgumentList $extra -WorkingDirectory $dir ` -WindowStyle Hidden -PassThru -RedirectStandardOutput $out -RedirectStandardError $err $ok = Wait-Up $port return @{ Proc = $p; Out = $out; Err = $err; Up = $ok } } function Stop-App($h) { if ($h -and $h.Proc) { Stop-Process -Id $h.Proc.Id -Force -ErrorAction SilentlyContinue } Start-Sleep -Milliseconds 600 } function New-CaseDir([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 } function Write-Conf([string]$dir, [string]$text) { [System.IO.File]::WriteAllText((Join-Path $dir 'daili.ini'), $text, (New-Object System.Text.UTF8Encoding($false))) } if (-not $LanIp) { $LanIp = @(Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' -and $_.PrefixOrigin -ne 'WellKnown' } | Select-Object -First 1 -ExpandProperty IPAddress) } if (-not $LanIp) { $LanIp = '192.168.1.1' } Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $Work | Out-Null Write-Host "`n=== 配置文件专项测试(LAN IP: $LanIp) ===" -ForegroundColor Cyan $c = New-Client # ---------- CFG-01 / CFG-02:首启生成默认配置 + 默认端口 17777 ---------- Write-Host "`n-- CFG-01/02 首次启动生成默认配置 --" -ForegroundColor Cyan $d1 = New-CaseDir 'default' $h = Start-App $d1 @('-no-open') 17777 $ini = Join-Path $d1 'daili.ini' Check 'CFG-01a' '首次启动自动生成 daili.ini' (Test-Path $ini) $iniText = Get-Content $ini -Raw -Encoding UTF8 Check 'CFG-01b' '默认配置含注释与默认监听' (($iniText -match '代打台 配置文件') -and ($iniText -match 'listen_ip = 0\.0\.0\.0') -and ($iniText -match 'listen_port = 17777') -and ($iniText -match 'screen_public = true')) $r = Get-Status $c 'http://127.0.0.1:17777/healthz' Check 'CFG-02' '默认监听 17777 可用' ($h.Up -and $r.Code -eq 200) "health=$($r.Code)" Stop-App $h $logDir = Join-Path $d1 'logs' $logs = @(Get-ChildItem $logDir -Filter 'daili-*.log' -ErrorAction SilentlyContinue) $logText = '' if ($logs.Count -ge 1) { $logText = Get-Content $logs[0].FullName -Raw -Encoding UTF8 } Check 'CFG-07' '运行日志按天落盘' ($logs.Count -ge 1 -and $logText.Length -gt 20) "files=$($logs.Count) bytes=$($logText.Length)" # ---------- CFG-03:只监听本机 ---------- Write-Host "`n-- CFG-03 仅本机监听 --" -ForegroundColor Cyan $d2 = New-CaseDir 'loopback' Write-Conf $d2 @" listen_ip = 127.0.0.1 listen_port = 17901 open_browser = false log_enabled = false backup_enabled = false "@ $h = Start-App $d2 @('-no-open') 17901 $local = (Test-NetConnection -ComputerName 127.0.0.1 -Port 17901 -InformationLevel Quiet -WarningAction SilentlyContinue) $lan = (Test-NetConnection -ComputerName $LanIp -Port 17901 -InformationLevel Quiet -WarningAction SilentlyContinue) Check 'CFG-03' '127.0.0.1 只开本机,局域网不可连' ($h.Up -and $local -and -not $lan) "local=$local lan=$lan" $banner = Get-Content (Join-Path $d2 'stdout.log') -Raw -Encoding UTF8 Check 'CFG-03b' '横幅不再打印局域网地址' (-not ($banner -match '局域网')) "" Stop-App $h # ---------- CFG-04:非法配置报错退出且不改文件 ---------- Write-Host "`n-- CFG-04 非法配置 --" -ForegroundColor Cyan $d3 = New-CaseDir 'invalid' Write-Conf $d3 "# 故意写错`nlisten_ip = 0.0.0.0`nlisten_port = 99999`n" $badIni = Join-Path $d3 'daili.ini' $hashBefore = (Get-FileHash $badIni -Algorithm SHA256).Hash $outText = & (Join-Path $d3 'daili.exe') -no-open 2>&1 | Out-String $code = $LASTEXITCODE $hashAfter = (Get-FileHash $badIni -Algorithm SHA256).Hash Check 'CFG-04a' '非法配置退出码为 1' ($code -eq 1) "code=$code" Check 'CFG-04b' '错误信息指出行号' ($outText -match '第 3 行' -and $outText -match '1-65535') "" Check 'CFG-04c' '原配置文件未被改动' ($hashBefore -eq $hashAfter) "" # ---------- CFG-05:命令行覆盖配置 ---------- Write-Host "`n-- CFG-05 命令行覆盖 --" -ForegroundColor Cyan $d4 = New-CaseDir 'override' Write-Conf $d4 @" listen_ip = 127.0.0.1 listen_port = 17902 open_browser = false log_enabled = false backup_enabled = false "@ $h = Start-App $d4 @('-no-open', '-addr', '127.0.0.1:17903') 17903 $p17902 = (Test-NetConnection -ComputerName 127.0.0.1 -Port 17902 -InformationLevel Quiet -WarningAction SilentlyContinue) Check 'CFG-05' '-addr 覆盖配置文件端口' ($h.Up -and -not $p17902) "up17903=$($h.Up) up17902=$p17902" Stop-App $h # ---------- CFG-06:大屏鉴权开关 ---------- Write-Host "`n-- CFG-06 大屏访问控制 --" -ForegroundColor Cyan $d5 = New-CaseDir 'screen-private' Write-Conf $d5 @" listen_ip = 127.0.0.1 listen_port = 17904 open_browser = false screen_public = false log_enabled = false backup_enabled = false "@ $h = Start-App $d5 @('-no-open') 17904 $anon = New-Client $r = Get-Status $anon 'http://127.0.0.1:17904/screen' Check 'CFG-06' 'screen_public=false 时大屏需登录' (($r.Path -like '/login*') -or ($r.Path -eq '/setup')) "path=$($r.Path)" Stop-App $h # ---------- CFG-08:备份目录与保留份数来自配置 ---------- Write-Host "`n-- CFG-08 备份目录与保留份数 --" -ForegroundColor Cyan $d6 = New-CaseDir 'backup' $bdir = Join-Path $d6 'my-backup' Write-Conf $d6 @" listen_ip = 127.0.0.1 listen_port = 17905 open_browser = false log_enabled = false backup_enabled = true backup_dir = $bdir backup_keep = 1 "@ $h = Start-App $d6 @('-no-open') 17905 $c6 = New-Client Post-Form $c6 'http://127.0.0.1:17905/setup' @{ username = 'admin'; display_name = '老板'; password = 'daili123'; password2 = 'daili123' } | Out-Null Post-Form $c6 'http://127.0.0.1:17905/packages' @{ name = '包月日常'; unit = 'day'; default_amount = '30'; color = '#2F5D50'; sort_order = '1' } | Out-Null Post-Form $c6 'http://127.0.0.1:17905/customers' @{ tail = '3001'; name = '备份客户'; game_name = ''; game_server = ''; contact = ''; tags = ''; source = ''; note = '' } | Out-Null Start-Sleep -Milliseconds 1200 Post-Form $c6 'http://127.0.0.1:17905/settings/backup' @{} | Out-Null Start-Sleep -Milliseconds 1200 Post-Form $c6 'http://127.0.0.1:17905/settings/backup' @{} | Out-Null $files = @(Get-ChildItem $bdir -Filter '*.db' -ErrorAction SilentlyContinue) Check 'CFG-08a' '备份写入配置指定的目录' ($files.Count -ge 1) "dir=$bdir files=$($files.Count)" Check 'CFG-08b' 'backup_keep=1 时只保留 1 份' ($files.Count -eq 1) "files=$($files.Count)" Stop-App $h # ---------- CFG-09:时区固定 ---------- Write-Host "`n-- CFG-09 时区固定 --" -ForegroundColor Cyan $d7 = New-CaseDir 'timezone' Write-Conf $d7 @" listen_ip = 127.0.0.1 listen_port = 17906 open_browser = false log_enabled = true backup_enabled = false timezone = Asia/Shanghai "@ $h = Start-App $d7 @('-no-open') 17906 $errText = Get-Content (Join-Path $d7 'stderr.log') -Raw -Encoding UTF8 Check 'CFG-09' '时区固定为 Asia/Shanghai 生效' ($errText -match '时区已固定为 Asia/Shanghai') "" Stop-App $h # ---------- 汇总 ---------- $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 $Evidence | Out-Null $script:results | Set-Content (Join-Path $Evidence 'config-result.txt') -Encoding UTF8 Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue if ($fail -gt 0) { exit 1 } exit 0