P0-P3 全量重构:退出码 / 解析修复、manifest 与 7z t 校验、干跑、排除规则、日志、测试与计划任务
P0 正确性 - 退出码:改用 .NET Process 直接启动、让子进程继承控制台,不再用 Start-Process -PassThru (在 7.7.0-preview.4 上 ExitCode 恒为 $null,会把成功的压缩判成失败); 7z / RAR / tar 三条解压分支统一走同一个取退出码的封装。 - BackupList 解析:先按第一个 :: 切段再处理引号(整行被一对引号包住的写法不再把排除表 吞进路径);排除表同时接受 , 与 ;(旧实现只认 ;,导致排除从未生效);支持 :- / :+ / @flag。 - 补回 .ssh 与孤儿归档:.ssh 进清单;孤儿归档在备份端也做审计并点名; 带 -Only / -Skip 时不再把未选中的归档误报成孤儿。 - Resolve-BackupEntry 里 $rootName 在赋值前被引用(会读到外层作用域残留值),已提前赋值。 P1 归档可靠性 - 每个条目写进 manifest.json:源、归档、时间、退出码、校验结果、失败原因, 并区分 warnings(在位归档)与 attemptWarnings(本次尝试)。 - 归档后做 7z t 内容校验,先写 .tmp、校验通过再原子替换(File.Move overwrite)。 - manifest.roots 记录归档内**真实**的顶层条目名(原先记的是软件名,Edge 实际是 "User Data")。 P2 可用性 - Restore 支持 -WhatIf / -DryRun / -VerifyOnly / -Only / -Skip; 这三种"只看不写"的模式一个字节都不写(原先会写回 manifest.json)。 - Edge 等高缓存条目加排除规则并实测:1781 MB / 27961 项 -> 72 MB / 2294 项; 书签、密码、Cookies、偏好、历史、IndexedDB、Local Storage 全部保留。 普通模式是相对归档根目录锚定的,嵌套的那些(如 OneAuth\WebView2 里的 Crashpad) 改用 ! 组件形式才会命中。 - 日志落盘 logs/<backup|restore>-<时间戳>.log;退出码按失败数返回。 - tools/Register-BackupTask.ps1 注册每日计划任务;tools/Rename-Archives.ps1 迁移旧归档名。 - root= 标记此前静默失效,现在明确告警(该功能尚未实现)。 P3 测试与验证 - tests/BakNRet.Tests.ps1:Pester 5 套件 62 项(含用子进程跑 Backup.ps1 / Restore.ps1 的端到端与针对上述缺陷的回归)。 - tests/Run-Pester.ps1 + tools/Install-TestDependencies.ps1:把 Pester 装到仓库内 .tools/, 不动机器上的全局模块(系统自带的 3.4.0 缺 Should -Be)。 - tests/Restore-Drill.ps1:真实归档恢复演练,明确区分"源在备份后变过"与"归档/解压有问题"。 - tests/Run-Tests.ps1(49 项,零依赖)与 tests/Run-E2E.ps1(23 项)继续可用;三套共 134 项全通过。 真实机器验证 - 生产归档 22/22 通过 7z t;-VerifyOnly 不再改动 manifest.json(SHA256 前后一致)。 - 真实恢复演练 12/12 通过,27,670 个文件与活源逐字节一致。 - 修复了生产 scoop-persist.7z:原先只有 90 字节(空归档)而源有 1.3 GB, 重打包后 233 MB,恢复演练 26981/26981 全部一致。
This commit is contained in:
1 parent
045d51ac9c
commit
e114cae8c8
13 files changed
+1761
-142
No files matched your search
@@ -0,0 +1,85 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
跑 BakNRet 的 Pester 测试套件(tests/BakNRet.Tests.ps1)。
|
||||
|
||||
.DESCRIPTION
|
||||
Pester 从哪儿来,按优先级:
|
||||
1. 仓库内的 .tools\modules(由 tools/Install-TestDependencies.ps1 放的本地副本,已 gitignore);
|
||||
2. 机器上已安装的 Pester 5+。
|
||||
|
||||
两者都没有时给出一条明确的安装命令并以退出码 2 结束,而不是抛一堆看不懂的错。
|
||||
|
||||
为什么显式要求 Pester 5.0+:3.4.0 没有 `Should -Be`,本套件会直接语法错误。
|
||||
本脚本只把 .tools\modules 临时加进 PSModulePath,**不会**去改机器上的全局模块。
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File .\tests\Run-Pester.ps1
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File .\tests\Run-Pester.ps1 -Verbosity Minimal
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('Detailed', 'Normal', 'Minimal', 'None')]
|
||||
[string]$Verbosity = 'Detailed',
|
||||
|
||||
[string[]]$Tag,
|
||||
|
||||
[string]$TestPath = (Join-Path $PSScriptRoot 'BakNRet.Tests.ps1')
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
||||
$localModules = Join-Path $projectRoot '.tools\modules'
|
||||
if (Test-Path -LiteralPath $localModules) {
|
||||
$env:PSModulePath = $localModules + [System.IO.Path]::PathSeparator + $env:PSModulePath
|
||||
}
|
||||
|
||||
$pester = Get-Module -ListAvailable Pester |
|
||||
Where-Object { [version]$_.Version -ge [version]'5.0.0' } |
|
||||
Sort-Object { [version]$_.Version } -Descending |
|
||||
Select-Object -First 1
|
||||
|
||||
if (-not $pester) {
|
||||
$installed = @(Get-Module -ListAvailable Pester | ForEach-Object { $_.Version.ToString() })
|
||||
|
||||
Write-Host ''
|
||||
Write-Host '找不到 Pester 5.0+,无法运行 Pester 套件。' -ForegroundColor Red
|
||||
if ($installed.Count -gt 0) {
|
||||
Write-Host ("已安装的版本:{0}(3.x 没有 Should -Be,本套件会语法错误)" -f ($installed -join '、')) -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ''
|
||||
Write-Host '两种拿到 Pester 5+ 的方式(任选其一):' -ForegroundColor Cyan
|
||||
Write-Host ' A. 只装到仓库里(推荐,不动机器上的全局模块):' -ForegroundColor Cyan
|
||||
Write-Host ' .\tools\Install-TestDependencies.ps1' -ForegroundColor Gray
|
||||
Write-Host ' B. 装到当前用户:' -ForegroundColor Cyan
|
||||
Write-Host ' Install-Module Pester -Scope CurrentUser -MinimumVersion 5.0.0' -ForegroundColor Gray
|
||||
Write-Host ''
|
||||
Write-Host '零依赖的替代方案:.\tests\Run-Tests.ps1(不需要 Pester)' -ForegroundColor Cyan
|
||||
Write-Host ''
|
||||
exit 2
|
||||
}
|
||||
|
||||
Import-Module $pester.Path -Force
|
||||
Write-Host ("Pester {0} ({1})" -f $pester.Version, $pester.ModuleBase) -ForegroundColor DarkGray
|
||||
Write-Host ("测试文件:{0}" -f $TestPath) -ForegroundColor DarkGray
|
||||
|
||||
$configuration = New-PesterConfiguration
|
||||
$configuration.Run.Path = $TestPath
|
||||
$configuration.Run.PassThru = $true
|
||||
$configuration.Run.Exit = $false
|
||||
$configuration.Output.Verbosity = $Verbosity
|
||||
if ($Tag) { $configuration.Filter.Tag = $Tag }
|
||||
|
||||
$result = Invoke-Pester -Configuration $configuration
|
||||
|
||||
Write-Host ''
|
||||
if ($result.FailedCount -gt 0) {
|
||||
Write-Host ("Pester 测试通过 {0} 项,失败 {1} 项,跳过 {2} 项" -f $result.PassedCount, $result.FailedCount, $result.SkippedCount) -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ("Pester 测试全部通过:{0} 项(跳过 {1} 项)" -f $result.PassedCount, $result.SkippedCount) -ForegroundColor Green
|
||||
exit 0
|
||||
Reference in new issue
Block a user