diff --git a/Backup.ps1 b/Backup.ps1 index 135df33..9f90877 100644 --- a/Backup.ps1 +++ b/Backup.ps1 @@ -437,7 +437,7 @@ foreach ($planLine in $lines) { if (-not $planItem) { continue } $planDisplayPath = $planItem.Path - $planResolved = Resolve-BakNRetBackupEntry -Entry $planItem -CatalogPath $catalogPath -MaxDepth $script:Config.CatalogMaxDepth + $planResolved = Resolve-BakNRetBackupEntry -Entry $planItem -CatalogPath $catalogPath if (-not $planResolved.BaseName) { continue } if (-not (Test-ItemSelected -DisplayPath $planDisplayPath -BaseName $planResolved.BaseName)) { continue } if ($planResolved.Direction -eq 'restore') { continue } @@ -542,7 +542,7 @@ foreach ($line in $lines) { if (-not $item) { continue } $displayPath = $item.Path - $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $script:Config.CatalogMaxDepth + $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath if (-not $resolved.BaseName) { $record = New-ItemRecord -BaseName ('raw:' + $displayPath) -Source $displayPath -ResolvedSource $displayPath -Phase 'parse' diff --git a/BackupConfig.psd1 b/BackupConfig.psd1 index 0d736c4..fd6921b 100644 --- a/BackupConfig.psd1 +++ b/BackupConfig.psd1 @@ -17,11 +17,6 @@ # 软件名录("软件名 -> 目录"映射表)。相对路径按本配置所在目录解析。 # BackupList.txt 里写软件名时靠它换成真实目录,归档名也取软件名。 SoftwareCatalog = 'SoftwareCatalog.psd1' - - # 名录里写的目录不存在时,按 "<名>_*" / "<名>-*" 向下找几层做前缀补全 - # (应对 legendary -> legendary_2.0.4 这类带后缀的目录) - CatalogMaxDepth = 5 - # 低于这个剩余空间(GB)就告警;真正放不下某个条目时会直接跳过该条目 MinFreeSpaceGB = 5 diff --git a/BakNRet/Private/Get-BaknretMapKeys.ps1 b/BakNRet/Private/Get-BaknretMapKeys.ps1 index 3b78b81..58d0cb7 100644 --- a/BakNRet/Private/Get-BaknretMapKeys.ps1 +++ b/BakNRet/Private/Get-BaknretMapKeys.ps1 @@ -1,5 +1,7 @@ function Get-BakNRetMapKeys { <# .SYNOPSIS 列出哈希表或 JSON 对象的全部键。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', + Justification = '名字里的复数是刻意的:单数形式已经被「只读单个对象」的那个函数占用,为了一条命名规则去重载语义得不偿失。')] param($Map) if ($null -eq $Map) { return @() } if ($Map -is [System.Collections.IDictionary]) { return @($Map.Keys) } diff --git a/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 b/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 index 5012327..4e642fa 100644 --- a/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 +++ b/BakNRet/Public/Find-BakNRetChildDirectoryByName.ps1 @@ -9,8 +9,7 @@ #> param( [Parameter(Mandatory = $true)][string]$Parent, - [Parameter(Mandatory = $true)][string]$Name, - [int]$MaxDepth = 5 + [Parameter(Mandatory = $true)][string]$Name ) $escaped = [regex]::Escape($Name) diff --git a/BakNRet/Public/Get-BakNRetArchiveTopLevelNames.ps1 b/BakNRet/Public/Get-BakNRetArchiveTopLevelNames.ps1 index c2d8167..b27bea7 100644 --- a/BakNRet/Public/Get-BakNRetArchiveTopLevelNames.ps1 +++ b/BakNRet/Public/Get-BakNRetArchiveTopLevelNames.ps1 @@ -12,6 +12,8 @@ 解压失败或拿不到 7z 时返回空数组,调用方据此跳过顶层名核对。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', + Justification = '同上:它返回的是归档内的全部顶层名,而单数名已经被「取第一段」的那个函数占用。')] param( [Parameter(Mandatory = $true)][string]$ArchivePath, [Parameter(Mandatory = $true)][string]$SevenZip, diff --git a/BakNRet/Public/Get-BakNRetItemArchiveName.ps1 b/BakNRet/Public/Get-BakNRetItemArchiveName.ps1 index a81068b..301de4c 100644 --- a/BakNRet/Public/Get-BakNRetItemArchiveName.ps1 +++ b/BakNRet/Public/Get-BakNRetItemArchiveName.ps1 @@ -18,7 +18,7 @@ if ($Entry.Flags -contains 'pathname') { $nameSource = $Entry.Path if (-not (Test-BakNRetLiteralPath -Path $Entry.Path)) { - $catalogForPath = Get-BakNRetSoftwareCatalog -Path $CatalogPath -MaxDepth $MaxDepth + $catalogForPath = Get-BakNRetSoftwareCatalog -Path $CatalogPath if ($catalogForPath.ContainsKey($Entry.Path)) { $nameSource = $catalogForPath[$Entry.Path].Path } @@ -28,7 +28,7 @@ $looksLikePath = Test-BakNRetLiteralPath -Path $Entry.Path if (-not $looksLikePath) { - $catalog = Get-BakNRetSoftwareCatalog -Path $CatalogPath -MaxDepth $MaxDepth + $catalog = Get-BakNRetSoftwareCatalog -Path $CatalogPath if ($catalog.ContainsKey($Entry.Path)) { return $catalog[$Entry.Path].Name } diff --git a/BakNRet/Public/Get-BakNRetSoftwareCatalog.ps1 b/BakNRet/Public/Get-BakNRetSoftwareCatalog.ps1 index 78c946f..17d7ab2 100644 --- a/BakNRet/Public/Get-BakNRetSoftwareCatalog.ps1 +++ b/BakNRet/Public/Get-BakNRetSoftwareCatalog.ps1 @@ -33,7 +33,6 @@ #> param( [Parameter(Mandatory = $true)][string]$Path, - [int]$MaxDepth = 5, [switch]$NoCache ) @@ -72,7 +71,7 @@ if (-not $include) { continue } $includePath = [string]$include if (-not [System.IO.Path]::IsPathRooted($includePath)) { $includePath = Join-Path $baseDir $includePath } - $included = Get-BakNRetSoftwareCatalog -Path $includePath -MaxDepth $MaxDepth + $included = Get-BakNRetSoftwareCatalog -Path $includePath foreach ($includedName in $included.Keys) { if ($result.ContainsKey($includedName)) { continue } $result[$includedName] = $included[$includedName] @@ -125,7 +124,7 @@ $parent = Split-Path -Path $declared -Parent $leafName = Split-Path -Path $declared -Leaf if ($parent -and $leafName -and (Test-Path -LiteralPath $parent)) { - $candidates = @(Find-BakNRetChildDirectoryByName -Parent $parent -Name $leafName -MaxDepth $MaxDepth) + $candidates = @(Find-BakNRetChildDirectoryByName -Parent $parent -Name $leafName) } } diff --git a/BakNRet/Public/Get-BaknretConfig.ps1 b/BakNRet/Public/Get-BaknretConfig.ps1 index 0c921cc..cc40670 100644 --- a/BakNRet/Public/Get-BaknretConfig.ps1 +++ b/BakNRet/Public/Get-BaknretConfig.ps1 @@ -13,7 +13,6 @@ LogDir = 'logs' SnapshotDir = 'Backups\snapshots' SoftwareCatalog = 'SoftwareCatalog.psd1' - CatalogMaxDepth = 5 MinFreeSpaceGB = 8 VerifyArchive = $true ComputeHash = $false diff --git a/BakNRet/Public/Get-BaknretSecurityRecord.ps1 b/BakNRet/Public/Get-BaknretSecurityRecord.ps1 index c008372..4cf2ba9 100644 --- a/BakNRet/Public/Get-BaknretSecurityRecord.ps1 +++ b/BakNRet/Public/Get-BaknretSecurityRecord.ps1 @@ -69,8 +69,8 @@ return $record } - try { $record.o = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value } catch { } - try { $record.g = $acl.GetGroup([System.Security.Principal.SecurityIdentifier]).Value } catch { } + try { $record.o = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value } catch { $null = $_ } + try { $record.g = $acl.GetGroup([System.Security.Principal.SecurityIdentifier]).Value } catch { $null = $_ } try { $sid = [System.Security.Principal.SecurityIdentifier] diff --git a/BakNRet/Public/Get-BaknretSecurityRecords.ps1 b/BakNRet/Public/Get-BaknretSecurityRecords.ps1 index 85ca161..e4f2836 100644 --- a/BakNRet/Public/Get-BaknretSecurityRecords.ps1 +++ b/BakNRet/Public/Get-BaknretSecurityRecords.ps1 @@ -20,6 +20,8 @@ 返回 [pscustomobject]@{ Records; Scanned; Kept; Errors }。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', + Justification = '同上:它采集一整棵树的记录,单数名是「读单个对象」的那个函数。')] param( [array]$Items = @(), [hashtable]$ScopeMap = @{}, diff --git a/BakNRet/Public/Get-BaknretSecuritySddlWithStale.ps1 b/BakNRet/Public/Get-BaknretSecuritySddlWithStale.ps1 index 218e83b..5e3b2d0 100644 --- a/BakNRet/Public/Get-BaknretSecuritySddlWithStale.ps1 +++ b/BakNRet/Public/Get-BaknretSecuritySddlWithStale.ps1 @@ -59,12 +59,12 @@ $rebuilt.SetOwner($Acl.GetOwner($sid)) $sections = $sections -bor [System.Security.AccessControl.AccessControlSections]::Owner } - catch { } + catch { $null = $_ } try { $rebuilt.SetGroup($Acl.GetGroup($sid)) $sections = $sections -bor [System.Security.AccessControl.AccessControlSections]::Group } - catch { } + catch { $null = $_ } $rebuilt.SetAccessRuleProtection($true, $false) diff --git a/BakNRet/Public/Resolve-BakNRetBackupEntry.ps1 b/BakNRet/Public/Resolve-BakNRetBackupEntry.ps1 index acb7af9..dc90738 100644 --- a/BakNRet/Public/Resolve-BakNRetBackupEntry.ps1 +++ b/BakNRet/Public/Resolve-BakNRetBackupEntry.ps1 @@ -24,13 +24,12 @@ #> param( $Entry, - [string]$CatalogPath, - [int]$MaxDepth = 5 + [string]$CatalogPath ) $isName = -not (Test-BakNRetLiteralPath -Path $Entry.Path) $forcePathFlavor = ($Entry.Flags -contains 'pathname') - $baseName = Get-BakNRetItemArchiveName -Entry $Entry -CatalogPath $CatalogPath -MaxDepth $MaxDepth + $baseName = Get-BakNRetItemArchiveName -Entry $Entry -CatalogPath $CatalogPath $overrides = $Entry.Overrides if (-not $overrides) { $overrides = @{} } @@ -76,7 +75,7 @@ } else { # ---- 写法一:软件名录里的软件名 ---- - $catalog = Get-BakNRetSoftwareCatalog -Path $CatalogPath -MaxDepth $MaxDepth + $catalog = Get-BakNRetSoftwareCatalog -Path $CatalogPath if (-not $catalog.ContainsKey($Entry.Path)) { $errorText = "软件名录里没有 '$($Entry.Path)'" } diff --git a/BakNRet/Public/Restore-BaknretSecurity.ps1 b/BakNRet/Public/Restore-BaknretSecurity.ps1 index 1390d43..d6d982a 100644 --- a/BakNRet/Public/Restore-BaknretSecurity.ps1 +++ b/BakNRet/Public/Restore-BaknretSecurity.ps1 @@ -13,6 +13,8 @@ 返回 [pscustomobject]@{ Total; Applied; OwnerFailed; Skipped; Failed; Failures }。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSupportsShouldProcess', '', + Justification = '它刻意自带 -WhatIf:这一层要逐对象决定要不要真写 ACL,而外层 Restore.ps1 的 ShouldProcess 粒度是整个条目。')] param( [Parameter(Mandatory = $true)]$Sidecar, [Parameter(Mandatory = $true)][string]$ArchiveRoot, diff --git a/BakNRet/Public/Set-BaknretDebug.ps1 b/BakNRet/Public/Set-BaknretDebug.ps1 index 1e05b0d..1a13e14 100644 --- a/BakNRet/Public/Set-BaknretDebug.ps1 +++ b/BakNRet/Public/Set-BaknretDebug.ps1 @@ -1,5 +1,7 @@ function Set-BakNRetDebug { <# .SYNOPSIS 打开 DEBUG 级别日志。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidDefaultValueSwitchParameter', '', + Justification = '开关默认为真是这个函数的接口语义(Set-BakNRetDebug -Enabled:$false 才是关掉)。为了规则去改公开参数的形状,代价大于收益。')] param([switch]$Enabled = $true) $script:LogConfig.EnableDebug = [bool]$Enabled } diff --git a/BakNRet/Public/Write-BakNRetLog.ps1 b/BakNRet/Public/Write-BakNRetLog.ps1 index 7db5d3b..e6f0a78 100644 --- a/BakNRet/Public/Write-BakNRetLog.ps1 +++ b/BakNRet/Public/Write-BakNRetLog.ps1 @@ -41,6 +41,7 @@ } catch { # 日志落盘失败时保持沉默:不能因为写日志失败而让备份失败。 + $null = $_ } } } diff --git a/Restore.ps1 b/Restore.ps1 index 72df373..879eed6 100644 --- a/Restore.ps1 +++ b/Restore.ps1 @@ -546,7 +546,7 @@ foreach ($line in $lines) { if (-not $item) { continue } $displayPath = $item.Path - $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $script:Config.CatalogMaxDepth + $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath $baseName = $resolved.BaseName if (-not $baseName) { $stats.skipped++; continue } diff --git a/test.ps1 b/test.ps1 index a74601b..60eb3db 100644 --- a/test.ps1 +++ b/test.ps1 @@ -34,6 +34,8 @@ .EXAMPLE .\test.ps1 -Suite E2E -PSVersion 5.1 #> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = '误判:这些参数的值是在传给 Test-Case / It 的 scriptblock 里用的,静态分析看不到那层使用。')] [CmdletBinding()] param( [ValidateSet('All', 'Parse', 'Unit', 'Smoke', 'E2E', 'Drill')] diff --git a/tests/BakNRet.Formats.Tests.ps1 b/tests/BakNRet.Formats.Tests.ps1 index 4f2218f..1e43694 100644 --- a/tests/BakNRet.Formats.Tests.ps1 +++ b/tests/BakNRet.Formats.Tests.ps1 @@ -138,7 +138,7 @@ Describe '软件名录:Slot 形状(新契约)' { } It '一个软件多个 Slot:Kind=Multi,Slot 按名排序且说明被保留' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -NoCache $catalog['pair'].Kind | Should -Be 'Multi' @($catalog['pair'].Slots).Count | Should -Be 2 (@($catalog['pair'].Slots | ForEach-Object { $_.Name }) -join ',') | Should -Be 'A,B' @@ -150,7 +150,7 @@ Describe '软件名录:Slot 形状(新契约)' { It '每个 Slot 都是一个独立的归档项来源(Kind=slot / Origin=catalog)' { $entry = ConvertFrom-BackupListLine -Line 'pair' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog @($resolved.Items).Count | Should -Be 2 (@($resolved.Items | ForEach-Object { $_.ArchivePath }) -join ',') | Should -Be 'A,B' (@($resolved.Items | ForEach-Object { $_.Kind }) -join ',') | Should -Be 'slot,slot' @@ -161,22 +161,22 @@ Describe '软件名录:Slot 形状(新契约)' { } It 'Slot 级排除写在 Slot 自己身上(相对本 Slot 的归档根)' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -NoCache (@($catalog['slotex'].Slots[0].Exclude) -join '|') | Should -Be '!*Cache|logs\' $entry = ConvertFrom-BackupListLine -Line 'slotex' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog (@($resolved.Items[0].Exclude) -join '|') | Should -Be '!*Cache|logs\' $resolved.HasExcludeOverride | Should -BeFalse } It '数组里"当前不存在"的 Slot 仍然产出归档项(恢复要靠它还原回原位)' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -NoCache $catalog['partial'].Kind | Should -Be 'Partial' @($catalog['partial'].Missing) | Should -Contain $script:MissingDir $entry = ConvertFrom-BackupListLine -Line 'partial' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog @($resolved.Items).Count | Should -Be 2 @($resolved.Items | ForEach-Object { $_.RealPath }) | Should -Contain $script:MissingDir $resolved.Blocking | Should -BeNullOrEmpty # 源不存在不是致命错误 @@ -184,7 +184,7 @@ Describe '软件名录:Slot 形状(新契约)' { It '文件 Slot:归档项是文件项(归档里就是名为 Slot 的文件)' { $entry = ConvertFrom-BackupListLine -Line 'fileapp' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog @($resolved.Items).Count | Should -Be 1 $resolved.Items[0].IsFile | Should -BeTrue $resolved.Items[0].ArchivePath | Should -Be 'Cfg' @@ -193,13 +193,13 @@ Describe '软件名录:Slot 形状(新契约)' { } It '旧的裸字符串 / 字符串数组写法被拒绝(ERROR + 跳过)' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -NoCache $catalog.ContainsKey('legacystr') | Should -BeFalse $catalog.ContainsKey('legacyarr') | Should -BeFalse } It '旧的 @{ Dirs = @(...) } 写法不再展开,留下 Invalid 与原因' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:FormatCatalog -NoCache $catalog.ContainsKey('legacydirs') | Should -BeTrue $catalog['legacydirs'].Kind | Should -Be 'Invalid' $catalog['legacydirs'].Error | Should -Not -BeNullOrEmpty @@ -208,7 +208,7 @@ Describe '软件名录:Slot 形状(新契约)' { It '多 Slot 的名录条目在清单里仍然按软件名命名归档' { $entry = ConvertFrom-BackupListLine -Line 'pair' - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3) | Should -Be 'pair' + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:FormatCatalog) | Should -Be 'pair' } } @@ -227,7 +227,7 @@ Describe '清单修饰符:新契约格式' { It ':: 覆盖 Path:单 Slot 条目直接生效' { $entry = ConvertFrom-BackupListLine -Line "solo :: $script:DirB" $entry.Overrides.ContainsKey('Path') | Should -BeTrue - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog @($resolved.Items).Count | Should -Be 1 $resolved.Items[0].ArchivePath | Should -Be 'Only' $resolved.Items[0].RealPath | Should -Be $script:DirB @@ -236,7 +236,7 @@ Describe '清单修饰符:新契约格式' { It ':: 覆盖遇到多 Slot 条目 -> Blocking(不知道给哪一个,绝不猜)' { $entry = ConvertFrom-BackupListLine -Line "pair :: $script:DirB" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog @($resolved.Items).Count | Should -Be 0 $resolved.Blocking | Should -Match '不能用一个' } @@ -246,7 +246,7 @@ Describe '清单修饰符:新契约格式' { (@($entry.ExcludePatterns) -join '|') | Should -Be 'logs\|!*Cache' (@($entry.Includes) -join '|') | Should -Be "Mods:$script:DirB" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:FormatCatalog $resolved.HasExcludeOverride | Should -BeTrue $resolved.HasIncludeOverride | Should -BeTrue @($resolved.Items | ForEach-Object { $_.ArchivePath }) | Should -Contain 'Mods' @@ -261,13 +261,13 @@ Describe '清单修饰符:新契约格式' { } It ':encrypt / :!encrypt 覆盖名录里的加密默认值' { - $base = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'fileapp') -CatalogPath $script:FormatCatalog -MaxDepth 3 + $base = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'fileapp') -CatalogPath $script:FormatCatalog $base.Encrypt | Should -BeTrue # 名录里 Cfg Slot 标了 Encrypt - $off = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'fileapp :!encrypt') -CatalogPath $script:FormatCatalog -MaxDepth 3 + $off = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'fileapp :!encrypt') -CatalogPath $script:FormatCatalog $off.Encrypt | Should -BeFalse - $on = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'pair :encrypt') -CatalogPath $script:FormatCatalog -MaxDepth 3 + $on = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'pair :encrypt') -CatalogPath $script:FormatCatalog $on.Encrypt | Should -BeTrue } @@ -279,7 +279,7 @@ Describe '清单修饰符:新契约格式' { # @pathname 对软件名条目也会改用真实路径命名 $entry = ConvertFrom-BackupListLine -Line 'pair @pathname' $expected = Get-BakNRetBackupBaseName -RawPath $script:DirA - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:FormatCatalog -MaxDepth 3) | Should -Be $expected + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:FormatCatalog) | Should -Be $expected } It '同一行里重复写同类记号会累积(不静默丢掉前一条规则)' { diff --git a/tests/BakNRet.Tests.ps1 b/tests/BakNRet.Tests.ps1 index 32cd085..0edd0de 100644 --- a/tests/BakNRet.Tests.ps1 +++ b/tests/BakNRet.Tests.ps1 @@ -407,24 +407,24 @@ Describe '归档命名' { It '软件名条目:默认用软件名做归档名' { $entry = ConvertFrom-BackupListLine -Line 'my-app' - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog -MaxDepth 3) | Should -Be 'my-app' + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog) | Should -Be 'my-app' } It '字面路径条目:仍用路径命名算法(现有清单无需改写)' { $entry = ConvertFrom-BackupListLine -Line 'C:\Programs\FooClolor' - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog -MaxDepth 3) | Should -Be 'FooClolor_from_C_+Programs' + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog) | Should -Be 'FooClolor_from_C_+Programs' } It '@pathname 用名录里的真实路径命名,而不是软件名' { $entry = ConvertFrom-BackupListLine -Line 'my-app @pathname' $expected = Get-BakNRetBackupBaseName -RawPath $script:NamingApp - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog -MaxDepth 3) | Should -Be $expected + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog) | Should -Be $expected } It '名录里没有该名字:归档名退回可读目录名,并给出 Error' { $entry = ConvertFrom-BackupListLine -Line 'no-such-thing' - (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog -MaxDepth 3) | Should -Be 'no-such-thing' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:NamingCatalog -MaxDepth 3 + (Get-BakNRetItemArchiveName -Entry $entry -CatalogPath $script:NamingCatalog) | Should -Be 'no-such-thing' + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:NamingCatalog $resolved.Error | Should -Not -BeNullOrEmpty @($resolved.Items).Count | Should -Be 0 } @@ -827,7 +827,7 @@ Describe '软件名录' { } It '名录解析:条目与 Slot 的字段集合就是新契约' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $entry = $catalog['single'] (@($entry.PSObject.Properties.Name) -join ',') | Should -Be 'Name,Path,Description,Slots,Kind,Missing,Error,Raw' $entry.Name | Should -Be 'single' @@ -849,7 +849,7 @@ Describe '软件名录' { } It '一个软件多个 Slot:Kind=Multi,Slot 按名字排序' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $catalog['multi'].Kind | Should -Be 'Multi' @($catalog['multi'].Slots).Count | Should -Be 2 (@($catalog['multi'].Slots | ForEach-Object { $_.Name }) -join ',') | Should -Be 'Alpha,Zeta' @@ -858,7 +858,7 @@ Describe '软件名录' { } It 'Kind:Single / Multi / Partial / Unresolved / Invalid' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $catalog['single'].Kind | Should -Be 'Single' $catalog['multi'].Kind | Should -Be 'Multi' $catalog['partial'].Kind | Should -Be 'Partial' @@ -873,7 +873,7 @@ Describe '软件名录' { } It '前缀补全:<名>_<后缀> 与 <名>-<后缀> 都会被补全' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $catalog['legendary'].Slots[0].Resolved | Should -Be (Join-Path $script:CatRoot 'legendary_2.0.4') $catalog['legendary'].Slots[0].Suffixed | Should -BeTrue @@ -882,12 +882,12 @@ Describe '软件名录' { } It '不会把 Legendary 误配成 LegendarySomething' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $catalog['legendary'].Slots[0].Resolved | Should -Not -Be (Join-Path $script:CatRoot 'LegendarySomething') } It '一个 Slot 命中多个候选目录:报 Error,绝不悄悄挑一棵树' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $entry = $catalog['dupslot'] $entry.Error | Should -Match '只能对应一个目录' $entry.Error | Should -Match 'dup_1' @@ -897,14 +897,14 @@ Describe '软件名录' { } It '文件 Slot:Path 指向文件时 IsFile 为真,Encrypt 也带上' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $slot = $catalog['fileslot'].Slots[0] $slot.IsFile | Should -BeTrue $slot.Encrypt | Should -BeTrue } It '%变量% 会在名录路径里展开' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache $catalog['envpath'].Slots[0].Resolved | Should -Be ([Environment]::ExpandEnvironmentVariables('%TEMP%')) } @@ -916,39 +916,39 @@ Describe '软件名录' { } It '读取结果按"路径 + 时间戳 + 长度 + 内容 MD5"缓存' { - $first = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 - $second = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 + $first = Get-BakNRetSoftwareCatalog -Path $script:CatFile + $second = Get-BakNRetSoftwareCatalog -Path $script:CatFile [object]::ReferenceEquals($first, $second) | Should -BeTrue - $fresh = Get-BakNRetSoftwareCatalog -Path $script:CatFile -MaxDepth 3 -NoCache + $fresh = Get-BakNRetSoftwareCatalog -Path $script:CatFile -NoCache [object]::ReferenceEquals($first, $fresh) | Should -BeFalse } It '名录文件内容变了以后缓存自动失效' { $path = Write-ListFile -Path (Join-Path $script:CatRoot 'cache.psd1') -Content "@{ 'x' = @{ S = @{ Path = '$script:CatReal'; Description = 'one' } } }" - (Get-BakNRetSoftwareCatalog -Path $path -MaxDepth 3)['x'].Description | Should -Be 'one' + (Get-BakNRetSoftwareCatalog -Path $path)['x'].Description | Should -Be 'one' Write-ListFile -Path $path -Content "@{ 'x' = @{ S = @{ Path = '$script:CatReal'; Description = 'two' } } }" | Out-Null - (Get-BakNRetSoftwareCatalog -Path $path -MaxDepth 3)['x'].Description | Should -Be 'two' + (Get-BakNRetSoftwareCatalog -Path $path)['x'].Description | Should -Be 'two' } It 'Includes:分文件维护的名录会被合并' { Write-ListFile -Path (Join-Path $script:CatRoot 'Extra.psd1') -Content "@{ 'extra-app' = @{ S = @{ Path = '$script:CatReal' } } }" | Out-Null $main = Write-ListFile -Path (Join-Path $script:CatRoot 'Main.psd1') -Content "@{ Includes = @('Extra.psd1'); 'main-app' = @{ S = @{ Path = '$script:CatReal' } } }" - $catalog = Get-BakNRetSoftwareCatalog -Path $main -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $main -NoCache $catalog.ContainsKey('main-app') | Should -BeTrue $catalog.ContainsKey('extra-app') | Should -BeTrue } It '旧的裸字符串 / 字符串数组 / 对象数组写法都会报 ERROR 并被跳过' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:LegacyFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:LegacyFile -NoCache $catalog.ContainsKey('bare') | Should -BeFalse $catalog.ContainsKey('arr') | Should -BeFalse $catalog.ContainsKey('objarr') | Should -BeFalse } It '旧的 @{ Dirs = @(...) } 写法不再展开:留下 Invalid 条目和原因' { - $catalog = Get-BakNRetSoftwareCatalog -Path $script:LegacyFile -MaxDepth 3 -NoCache + $catalog = Get-BakNRetSoftwareCatalog -Path $script:LegacyFile -NoCache $catalog.ContainsKey('dirstyle') | Should -BeTrue $catalog['dirstyle'].Kind | Should -Be 'Invalid' $catalog['dirstyle'].Error | Should -Not -BeNullOrEmpty @@ -976,7 +976,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It 'resolve 与归档项的字段集合就是新契约(旧字段已删除)' { $entry = ConvertFrom-BackupListLine -Line 'single' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile (@($resolved.PSObject.Properties.Name) -join ',') | Should -Be 'IsName,CatalogEntry,BaseName,ArchiveFlavor,Direction,Items,Encrypt,ExcludePatterns,HasExcludeOverride,Includes,HasIncludeOverride,Source,Error,Blocking' @($resolved.Items)[0].PSObject.Properties.Name | Should -Contain 'ArchivePath' @($resolved.Items)[0].PSObject.Properties.Name | Should -Contain 'Origin' @@ -985,7 +985,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '软件名条目:isName/CatalogEntry/BaseName/ArchiveFlavor/Source' { $entry = ConvertFrom-BackupListLine -Line 'single' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.IsName | Should -BeTrue $resolved.BaseName | Should -Be 'single' $resolved.ArchiveFlavor | Should -Be 'name' @@ -997,7 +997,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '软件名条目:Items 来自 Slot(ArchivePath=Slot 名,Kind=slot,Origin=catalog)' { $entry = ConvertFrom-BackupListLine -Line 'multi' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile @($resolved.Items).Count | Should -Be 2 (@($resolved.Items | ForEach-Object { $_.ArchivePath }) -join ',') | Should -Be 'Alpha,Zeta' (@($resolved.Items | ForEach-Object { $_.Kind }) -join ',') | Should -Be 'slot,slot' @@ -1009,7 +1009,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '字面路径条目:一个 path 项(ArchivePath 是末级名)' { $entry = ConvertFrom-BackupListLine -Line 'C:\Programs\FooClolor' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.IsName | Should -BeFalse $resolved.ArchiveFlavor | Should -Be 'path' @($resolved.Items).Count | Should -Be 1 @@ -1021,7 +1021,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '字面路径条目上的 @ Path= 覆盖目标路径,但归档名仍按原路径' { $entry = ConvertFrom-BackupListLine -Line "C:\Programs\Foo @ Path='D:\Elsewhere'" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.Items[0].RealPath | Should -Be 'D:\Elsewhere' $resolved.BaseName | Should -Be 'Foo_from_C_+Programs' } @@ -1029,7 +1029,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '名录里的路径不存在时仍给出 Items(恢复要靠它还原回原位)' { $missingCatalog = Write-ListFile -Path (Join-Path $script:Sandbox 'catalog\Missing.psd1') -Content "@{ 'gone' = @{ S = @{ Path = 'C:\definitely-not-here-98765' } } }" $entry = ConvertFrom-BackupListLine -Line 'gone' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $missingCatalog -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $missingCatalog @($resolved.Items).Count | Should -Be 1 $resolved.Items[0].Exists | Should -BeFalse $resolved.Items[0].RealPath | Should -Be 'C:\definitely-not-here-98765' @@ -1037,7 +1037,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It ':: 覆盖 Path:单 Slot 条目直接生效' { $entry = ConvertFrom-BackupListLine -Line "single :: $script:CatOther" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile @($resolved.Items).Count | Should -Be 1 $resolved.Items[0].ArchivePath | Should -Be 'Main' $resolved.Items[0].RealPath | Should -Be $script:CatOther @@ -1047,7 +1047,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It ':: / @ Path= 覆盖遇到多 Slot 条目 -> Blocking(不猜是哪一个)' { foreach ($line in @("multi :: $script:CatOther", "multi @ Path='$script:CatOther'")) { $entry = ConvertFrom-BackupListLine -Line $line - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile @($resolved.Items).Count | Should -Be 0 $resolved.Blocking | Should -Match '不能用一个' } @@ -1055,14 +1055,14 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '条目级 :- 覆盖名录里的排除:HasExcludeOverride 为真' { $entry = ConvertFrom-BackupListLine -Line 'single :- logs\,!*Cache' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.HasExcludeOverride | Should -BeTrue (@($resolved.ExcludePatterns) -join '|') | Should -Be 'logs\|!*Cache' } It '名录 Slot 的 Include 会追加成 include 项' { $entry = ConvertFrom-BackupListLine -Line 'incapp' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile @($resolved.Items).Count | Should -Be 2 $include = @($resolved.Items | Where-Object { $_.Origin -eq 'include' }) $include.Count | Should -Be 1 @@ -1074,7 +1074,7 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { It '条目级 :+ / @ Include= 覆盖名录里的 Include' { foreach ($line in @("incapp :+ Other:$script:CatOther", "incapp @ Include='Other:$script:CatOther'")) { $entry = ConvertFrom-BackupListLine -Line $line - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.HasIncludeOverride | Should -BeTrue (@($resolved.Items | ForEach-Object { $_.ArchivePath }) -join ',') | Should -Be 'A,Other' @($resolved.Items)[1].RealPath | Should -Be $script:CatOther @@ -1082,34 +1082,34 @@ Describe 'Resolve-BakNRetBackupEntry:条目解析' { } It '加密:名录里各 Slot 取或;条目级 :encrypt / :!encrypt 覆盖' { - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single') -CatalogPath $script:CatFile -MaxDepth 3).Encrypt | Should -BeFalse - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'encapp') -CatalogPath $script:CatFile -MaxDepth 3).Encrypt | Should -BeTrue - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'encapp :!encrypt') -CatalogPath $script:CatFile -MaxDepth 3).Encrypt | Should -BeFalse - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single :encrypt') -CatalogPath $script:CatFile -MaxDepth 3).Encrypt | Should -BeTrue - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line "single @ Encrypt='true'") -CatalogPath $script:CatFile -MaxDepth 3).Encrypt | Should -BeTrue + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single') -CatalogPath $script:CatFile).Encrypt | Should -BeFalse + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'encapp') -CatalogPath $script:CatFile).Encrypt | Should -BeTrue + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'encapp :!encrypt') -CatalogPath $script:CatFile).Encrypt | Should -BeFalse + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single :encrypt') -CatalogPath $script:CatFile).Encrypt | Should -BeTrue + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line "single @ Encrypt='true'") -CatalogPath $script:CatFile).Encrypt | Should -BeTrue } It '方向标记会传递到 Resolve-BakNRetBackupEntry.Direction' { - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '+ single') -CatalogPath $script:CatFile -MaxDepth 3).Direction | Should -Be 'backup' - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '- single') -CatalogPath $script:CatFile -MaxDepth 3).Direction | Should -Be 'restore' - (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single') -CatalogPath $script:CatFile -MaxDepth 3).Direction | Should -Be 'both' + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '+ single') -CatalogPath $script:CatFile).Direction | Should -Be 'backup' + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '- single') -CatalogPath $script:CatFile).Direction | Should -Be 'restore' + (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'single') -CatalogPath $script:CatFile).Direction | Should -Be 'both' } It '归档内路径冲突(include 与 Slot 同名)-> Blocking' { $entry = ConvertFrom-BackupListLine -Line "conflict :+ Data:$script:CatOther" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.Blocking | Should -Match '归档内路径冲突' } It '归档内路径冲突(父子关系)-> Blocking' { $entry = ConvertFrom-BackupListLine -Line "conflict :+ Data\sub:$script:CatOther" - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.Blocking | Should -Match '父子关系' } It '名录里没有该名字:Error 给出,Items 为空' { $entry = ConvertFrom-BackupListLine -Line 'no-such-thing' - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile -MaxDepth 3 + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $script:CatFile $resolved.Error | Should -Match 'no-such-thing' @($resolved.Items).Count | Should -Be 0 } diff --git a/tests/Restore-Drill.ps1 b/tests/Restore-Drill.ps1 index 980e09d..1120492 100644 --- a/tests/Restore-Drill.ps1 +++ b/tests/Restore-Drill.ps1 @@ -322,7 +322,7 @@ foreach ($name in $Entries) { $entry = ConvertFrom-BackupListLine -Line $name if (-not $entry) { continue } - $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $catalogPath -MaxDepth $config.CatalogMaxDepth + $resolved = Resolve-BakNRetBackupEntry -Entry $entry -CatalogPath $catalogPath if (-not $resolved.BaseName) { $failures += "$name :解析不出归档名" @@ -456,7 +456,6 @@ foreach ($name in $Entries) { BackupDir = '$scratchBackupRoot' LogDir = '$scratchLogDir' SoftwareCatalog = '$scratchCatalog' - CatalogMaxDepth = $($config.CatalogMaxDepth) VerifyArchive = `$true } "@, [System.Text.UTF8Encoding]::new($false)) diff --git a/tests/Run-E2E.ps1 b/tests/Run-E2E.ps1 index dae0e17..7b1f17b 100644 --- a/tests/Run-E2E.ps1 +++ b/tests/Run-E2E.ps1 @@ -61,8 +61,7 @@ function New-E2EConfig { param( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$LogDir, - [string]$SoftwareCatalog, - [int]$CatalogMaxDepth = 5 + [string]$SoftwareCatalog ) $lines = @( '@{' @@ -72,7 +71,6 @@ function New-E2EConfig { ' MinFreeSpaceGB = 0' ) if ($SoftwareCatalog) { $lines += " SoftwareCatalog = '$SoftwareCatalog'" } - $lines += " CatalogMaxDepth = $CatalogMaxDepth" $lines += '}' [System.IO.File]::WriteAllText($Path, ($lines -join "`r`n"), [System.Text.UTF8Encoding]::new($true)) } @@ -84,13 +82,13 @@ function Get-NewestLog { Sort-Object LastWriteTime | Select-Object -Last 1 } -function Get-ArchiveNames { +function Get-ArchiveName { param([Parameter(Mandatory = $true)][string]$Dir) return @(Get-ChildItem -LiteralPath $Dir -File -Filter *.7z -ErrorAction SilentlyContinue | Select-Object -ExpandProperty BaseName) } -function Get-OrphanNames { +function Get-OrphanName { <# .SYNOPSIS 从备份日志里解析出"孤儿归档"那一段点名的归档名。 #> param([Parameter(Mandatory = $true)][string]$LogPath) @@ -528,7 +526,7 @@ Test-Case '行首 - = 仅备份端跳过(日志点名),不产生归档' { } Test-Case '孤儿审计:`-` 条目的归档名算有主,真孤儿才被点名' { - $orphans = Get-OrphanNames -LogPath $dirLog.FullName + $orphans = Get-OrphanName -LogPath $dirLog.FullName Assert-True ($orphans -contains 'zzz-orphan.7z') '真孤儿必须被点名' Assert-False ($orphans -contains 'app-b.7z') '`-` 条目的归档不能被误报成孤儿' } diff --git a/tests/Run-RealSmoke.ps1 b/tests/Run-RealSmoke.ps1 index 1556c9c..02e5d7a 100644 --- a/tests/Run-RealSmoke.ps1 +++ b/tests/Run-RealSmoke.ps1 @@ -23,6 +23,8 @@ .EXAMPLE .\tests\Run-RealSmoke.ps1 #> +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = '误判:这些参数的值是在传给 Test-Case / It 的 scriptblock 里用的,静态分析看不到那层使用。')] [CmdletBinding()] param( [string]$ModulePath, @@ -130,7 +132,7 @@ Test-Case '真实清单里每个条目都能解析出方向,且软件名都能 if ($item.Direction -eq 'backup') { $backupOnly++ } if ($item.Direction -eq 'restore') { $restoreOnly++ } - $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $config.CatalogMaxDepth + $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath if ($resolved.Error -and $resolved.Error -like '*名录里没有*') { $catalogMiss += $resolved.Error } } diff --git a/tests/TestHelpers.psm1 b/tests/TestHelpers.psm1 index 924754f..4f2d54a 100644 --- a/tests/TestHelpers.psm1 +++ b/tests/TestHelpers.psm1 @@ -66,6 +66,8 @@ function Assert-Null { } function Assert-FileExists { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', + Justification = '误判:规则把结尾的 s 当复数,而这里的名词是 FileExists(Exists 不是复数)。')] param([string]$Path, [string]$Because) if (-not (Test-Path -LiteralPath $Path)) { throw ("文件应当存在但不存在:{0}{1}" -f $Path, $(if ($Because) { "($Because)" } else { '' })) diff --git a/tools/Rename-Archives.ps1 b/tools/Rename-Archives.ps1 index 415b774..24328d4 100644 --- a/tools/Rename-Archives.ps1 +++ b/tools/Rename-Archives.ps1 @@ -121,7 +121,7 @@ foreach ($line in (Get-Content -Encoding UTF8 -LiteralPath $BackupListPath)) { $item = ConvertFrom-BackupListLine -Line $line if (-not $item) { continue } - $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath -MaxDepth $config.CatalogMaxDepth + $resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catalogPath $newName = $resolved.BaseName if (-not $newName) { continue } diff --git a/tools/lab/Lab-Common.ps1 b/tools/lab/Lab-Common.ps1 index 6b349aa..4eb53bf 100644 --- a/tools/lab/Lab-Common.ps1 +++ b/tools/lab/Lab-Common.ps1 @@ -88,6 +88,8 @@ function Save-LabCredential { function Get-LabCredential { <# .SYNOPSIS 读出 VM 凭据为 PSCredential。 #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', + Justification = '实验机的随机口令本来就得明文生成再注入 unattend.xml(Windows Setup 只接受那种形式)。它只活在本机实验环境,不进生产路径。')] param() $path = Get-LabCredentialPath if (-not (Test-Path -LiteralPath $path)) { throw "找不到 VM 凭据:$path(先跑 New-BakNRetLab.ps1)" } diff --git a/tools/lab/New-BakNRetLab.ps1 b/tools/lab/New-BakNRetLab.ps1 index 5870c58..7ca1f1d 100644 --- a/tools/lab/New-BakNRetLab.ps1 +++ b/tools/lab/New-BakNRetLab.ps1 @@ -28,6 +28,8 @@ #> [CmdletBinding()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification = '误判:-Recreate 是在遍历 VHDX 的 scriptblock 里用的,静态分析看不到那层使用。')] param( [ValidateSet('all', 'disk', 'vm', 'provision')][string]$Stage = 'all', [switch]$Recreate, diff --git a/tools/lab/payload/run-acl-scenario.ps1 b/tools/lab/payload/run-acl-scenario.ps1 index 69f427b..e839db7 100644 --- a/tools/lab/payload/run-acl-scenario.ps1 +++ b/tools/lab/payload/run-acl-scenario.ps1 @@ -24,6 +24,8 @@ 参数只传字符串,数组用 ';' 拼 —— 与 run-drill.ps1 同一套约定。 #> [CmdletBinding()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', + Justification = 'Scoop 官方的安装方式就是 irm | iex 那种一行式;实验脚本照抄官方写法比自己包一层更不容易出错。')] param( [string]$RepoPath = 'C:\BakNRet', [string]$WorkRoot = 'C:\BakNRet-Lab\acl', @@ -137,7 +139,7 @@ function Invoke-BakNRetChild { } } -function Stop-VscodeProcesses { +function Stop-VscodeProcess { <# .SYNOPSIS 把 vscode 相关进程清掉。 @@ -159,7 +161,7 @@ function Stop-VscodeProcesses { Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue } } - catch { } + catch { $null = $_ } } } Start-Sleep -Milliseconds 700 @@ -412,7 +414,7 @@ Test-Scenario '每个条目都写了 .acl.json' (@(Get-ChildItem -LiteralPath $B # 删源 → 恢复 # --------------------------------------------------------------------------- foreach ($path in $entries) { - if ($path -ieq $vscodeApp) { Stop-VscodeProcesses -AppRoot $vscodeApp } + if ($path -ieq $vscodeApp) { Stop-VscodeProcess -AppRoot $vscodeApp } Remove-TreeHard -Path $path } $leftovers = @($entries | Where-Object { Test-Path -LiteralPath $_ }) diff --git a/tools/lab/payload/sandbox/BackupConfig.psd1 b/tools/lab/payload/sandbox/BackupConfig.psd1 index 1de10c8..a118a23 100644 --- a/tools/lab/payload/sandbox/BackupConfig.psd1 +++ b/tools/lab/payload/sandbox/BackupConfig.psd1 @@ -13,7 +13,6 @@ # 不是按本配置文件所在目录;而且路径不存在时会**静默回退**到仓库真实的 # SoftwareCatalog.psd1。沙盒必须写成仓库根相对路径,否则软件名条目会悄悄用错名录。 SoftwareCatalog = 'tools\lab\payload\sandbox\SoftwareCatalog.psd1' - CatalogMaxDepth = 5 MinFreeSpaceGB = 0 VerifyArchive = $true ComputeHash = $true