Group all 69 projects into category subfolders under src/ and tests/ so the Rider Solution Explorer mirrors the module structure. Folders: Core, Server, Drivers (with a nested Driver CLIs subfolder), Client, Tooling. - Move every project folder on disk with git mv (history preserved as renames). - Recompute relative paths in 57 .csproj files: cross-category ProjectReferences, the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external mxaccessgw refs in Driver.Galaxy and its test project. - Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders. - Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL, integration, install). Build green (0 errors); unit tests pass. Docs left for a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
6.6 KiB
PowerShell
111 lines
6.6 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Phase 6.3 exit-gate compliance check. Each check either passes or records a
|
|
failure; non-zero exit = fail.
|
|
|
|
.DESCRIPTION
|
|
Validates Phase 6.3 (Redundancy runtime) completion. Checks enumerated in
|
|
`docs/v2/implementation/phase-6-3-redundancy-runtime.md`
|
|
§"Compliance Checks (run at exit gate)".
|
|
|
|
.NOTES
|
|
Usage: pwsh ./scripts/compliance/phase-6-3-compliance.ps1
|
|
Exit: 0 = all checks passed; non-zero = one or more FAILs
|
|
#>
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$script:failures = 0
|
|
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
|
|
function Assert-Pass { param([string]$C) Write-Host " [PASS] $C" -ForegroundColor Green }
|
|
function Assert-Fail { param([string]$C, [string]$R) Write-Host " [FAIL] $C - $R" -ForegroundColor Red; $script:failures++ }
|
|
function Assert-Deferred { param([string]$C, [string]$P) Write-Host " [DEFERRED] $C (follow-up: $P)" -ForegroundColor Yellow }
|
|
|
|
function Assert-FileExists {
|
|
param([string]$C, [string]$P)
|
|
if (Test-Path (Join-Path $repoRoot $P)) { Assert-Pass "$C ($P)" }
|
|
else { Assert-Fail $C "missing file: $P" }
|
|
}
|
|
|
|
function Assert-TextFound {
|
|
param([string]$C, [string]$Pat, [string[]]$Paths)
|
|
foreach ($p in $Paths) {
|
|
$full = Join-Path $repoRoot $p
|
|
if (-not (Test-Path $full)) { continue }
|
|
if (Select-String -Path $full -Pattern $Pat -Quiet) {
|
|
Assert-Pass "$C (matched in $p)"
|
|
return
|
|
}
|
|
}
|
|
Assert-Fail $C "pattern '$Pat' not found in any of: $($Paths -join ', ')"
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "=== Phase 6.3 compliance - Redundancy runtime ===" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
Write-Host "Stream B - ServiceLevel 8-state matrix (decision #154)"
|
|
Assert-FileExists "ServiceLevelCalculator present" "src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs"
|
|
Assert-FileExists "ServiceLevelBand enum present" "src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs"
|
|
Assert-TextFound "Maintenance = 0 (reserved per OPC UA Part 5)" "Maintenance\s*=\s*0" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "NoData = 1 (reserved per OPC UA Part 5)" "NoData\s*=\s*1" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "InvalidTopology = 2 (detected-inconsistency band)" "InvalidTopology\s*=\s*2" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "AuthoritativePrimary = 255" "AuthoritativePrimary\s*=\s*255" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "IsolatedPrimary = 230 (retains authority)" "IsolatedPrimary\s*=\s*230" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "PrimaryMidApply = 200" "PrimaryMidApply\s*=\s*200" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "RecoveringPrimary = 180" "RecoveringPrimary\s*=\s*180" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "AuthoritativeBackup = 100" "AuthoritativeBackup\s*=\s*100" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "IsolatedBackup = 80 (does NOT auto-promote)" "IsolatedBackup\s*=\s*80" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "BackupMidApply = 50" "BackupMidApply\s*=\s*50" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
Assert-TextFound "RecoveringBackup = 30" "RecoveringBackup\s*=\s*30" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ServiceLevelCalculator.cs")
|
|
|
|
Write-Host ""
|
|
Write-Host "Stream B - RecoveryStateManager"
|
|
Assert-FileExists "RecoveryStateManager present" "src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/RecoveryStateManager.cs"
|
|
Assert-TextFound "Dwell + publish-witness gate" "_witnessed" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/RecoveryStateManager.cs")
|
|
Assert-TextFound "Default dwell 60 s" "FromSeconds\(60\)" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/RecoveryStateManager.cs")
|
|
|
|
Write-Host ""
|
|
Write-Host "Stream D - Apply-lease registry (decision #162)"
|
|
Assert-FileExists "ApplyLeaseRegistry present" "src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ApplyLeaseRegistry.cs"
|
|
Assert-TextFound "BeginApplyLease returns IAsyncDisposable" "IAsyncDisposable" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ApplyLeaseRegistry.cs")
|
|
Assert-TextFound "Lease key includes PublishRequestId" "PublishRequestId" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ApplyLeaseRegistry.cs")
|
|
Assert-TextFound "Watchdog PruneStale present" "PruneStale" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ApplyLeaseRegistry.cs")
|
|
Assert-TextFound "Default ApplyMaxDuration 10 min" "FromMinutes\(10\)" @("src/Server/ZB.MOM.WW.OtOpcUa.Server/Redundancy/ApplyLeaseRegistry.cs")
|
|
|
|
Write-Host ""
|
|
Write-Host "Deferred surfaces"
|
|
Assert-Deferred "Stream A - RedundancyCoordinator cluster-topology loader" "task #145"
|
|
Assert-Deferred "Stream C - OPC UA node wiring (ServiceLevel + ServerUriArray + RedundancySupport)" "task #147"
|
|
Assert-Deferred "Stream E - Admin RedundancyTab + OpenTelemetry metrics + SignalR" "task #149"
|
|
Assert-Deferred "Stream F - Client interop matrix + Galaxy MXAccess failover" "task #150"
|
|
Assert-Deferred "sp_PublishGeneration rejects Transparent mode pre-publish" "task #148 part 2 (SQL-side validator)"
|
|
|
|
Write-Host ""
|
|
Write-Host "Cross-cutting"
|
|
Write-Host " Running full solution test suite..." -ForegroundColor DarkGray
|
|
$prevPref = $ErrorActionPreference
|
|
$ErrorActionPreference = 'Continue'
|
|
$testOutput = & dotnet test (Join-Path $repoRoot 'ZB.MOM.WW.OtOpcUa.slnx') --nologo 2>&1
|
|
$ErrorActionPreference = $prevPref
|
|
$passLine = $testOutput | Select-String 'Passed:\s+(\d+)' -AllMatches
|
|
$failLine = $testOutput | Select-String 'Failed:\s+(\d+)' -AllMatches
|
|
$passCount = 0; foreach ($m in $passLine.Matches) { $passCount += [int]$m.Groups[1].Value }
|
|
$failCount = 0; foreach ($m in $failLine.Matches) { $failCount += [int]$m.Groups[1].Value }
|
|
$baseline = 1097
|
|
if ($passCount -ge $baseline) { Assert-Pass "No test-count regression ($passCount >= $baseline pre-Phase-6.3 baseline)" }
|
|
else { Assert-Fail "Test-count regression" "passed $passCount < baseline $baseline" }
|
|
|
|
if ($failCount -le 1) { Assert-Pass "No new failing tests (pre-existing CLI flake tolerated)" }
|
|
else { Assert-Fail "New failing tests" "$failCount failures > 1 tolerated" }
|
|
|
|
Write-Host ""
|
|
if ($script:failures -eq 0) {
|
|
Write-Host "Phase 6.3 compliance: PASS" -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
Write-Host "Phase 6.3 compliance: $script:failures FAIL(s)" -ForegroundColor Red
|
|
exit 1
|