fix(backup,restore): 显式指定的清单不存在时报失败,不再静默成功

传 -BackupListPath 指向一个不存在的文件时,原实现把它当成"首次运行"处理:备份端会在
那个位置凭空建一份模板,恢复端则"从备份内容生成清单",然后两者都 exit 0。

后果在计划任务里最明显:任务计划程序读到的是"上次运行结果 = 成功",而实际上一个条目
都没处理。这与本仓库已经修过的"27 条被静默跳过、退出码仍是 0"是同一类缺陷 —— 退出码
是自动化唯一能读到的信号,它必须对得上"到底干了什么"。

现在按 $PSBoundParameters.ContainsKey('BackupListPath') 把两条语义分开:

  * 显式指定了清单却不存在 -> 报 ERROR 并 exit 1。路径写错、或相对路径按了别的工作
    目录解析(计划任务的工作目录通常是 C:\Windows\System32),都是调用方的错误,
    不能伪装成"已经帮你建好模板了"。
  * 没指定(首次运行引导)-> 保持原行为:备份端建模板、恢复端从 manifest 生成清单,
    仍然 exit 0。这是"开箱即用",不是失败。

顺带补 2 处运算符前的空格(Backup.ps1 / Restore.ps1 的 PSUseConsistentWhitespace)。

测试:
  * 2 条成对回归用例(显式缺失报失败 / 首次运行仍建模板)。必须成对 —— 只测一条的话,
    "把所有缺失都改成 exit 1"这种错误实现也能骗过测试。
  * 7 条断言覆盖归档原子替换与路径解析基准。其中 3 条用 AST 判定,因为文本匹配会被
    函数自己的注释绊倒(注释里正当地提到了 $PSScriptRoot,解释为什么不许用)。

验收:test.ps1 9/9 全绿(5.1 与 7);Pester 183 -> 192,0 失败;静态分析 80 条、
0 Error(与改造前持平)。
This commit is contained in:
Shuery committed 2026-10-02 01:16:25 +08:00
1 parent d72fe63c02
commit 7149ea691e
5 files changed
+181 -2

No files matched your search

+15
View File
@@ -134,6 +134,21 @@ if (-not (Test-Path -LiteralPath $BackupDir)) {
}
if (-not (Test-Path -LiteralPath $BackupListPath)) {
# 清单缺失有两条语义完全不同的路:
#
# ① 首次运行(没传 -BackupListPath,用的是仓库自带的那份)-> 建一份模板,退出 0。
# 这是「开箱即用」的引导,**不是失败**。
# ② 显式传了 -BackupListPath 却不存在 -> 是调用方的错误(路径写错、计划任务里
# 的相对路径按了别的工作目录解析)。此时绝不能建模板就退出 0:计划任务会读到
# 「上次运行结果 = 成功」,而实际上一个条目都没处理 —— 这个仓库刚把
# 「27 条被静默跳过、退出码仍是 0」当作一类缺陷修过,这条是同一类。
if ($PSBoundParameters.ContainsKey('BackupListPath')) {
Write-BakNRetLog ("指定的清单不存在:{0}" -f $BackupListPath) -Level ERROR
Write-BakNRetLog '显式指定 -BackupListPath 时不会自动创建模板:请检查路径是否写错;要生成一份起步模板,就去掉该参数。' -Level ERROR
Stop-BakNRetLog
exit 1
}
$template = "# BackupList.txt`n" +
"# 语法: [+|-] <软件名 或 绝对路径> [:: <路径>] [:- <排除模式>[,<排除模式>...]] [:+ <追加项>[,<追加项>...]] [:encrypt | :!encrypt] [@ <Key>='<值>'] [# 说明]`n" +
"# 示例: Edge`n" +