zscripts-token-savers/tests/VerifyPlan.Tests.ps1
Kelly Michels ac95c47255
chore(sync): bring zdeploy/ZHelpers up to the private tree, sanitized (#64)
The mirror's deploy pair had drifted ~280 lines behind: it lacked the
transactional .env preserve/restore (an interrupted deploy could destroy
server-side env files), the stderr-flattening step wrapper (a successful
deploy reported failure and skipped its own verification), and the
verification rework (channel re-picked every retry, edge only with a Host
to route by, verify.timeoutSeconds, honest split of "stale build" vs "no
channel answered").

The sync is byte-faithful to the private tree except where the mirror's
own Sanitization suite demands otherwise - and it caught the first copy:
three failures for private project names, a private domain, and a
private-only script name that rode along in comments. Each war story keeps
its lesson and loses its cast, per the convention already in the file
("EvoCivilCode: deploy/" was already published as "(deploy/, infra/,
...)"). That suite going red on an unsanitized copy is exactly what it
exists for.

Also in this change:

- tests/VerifyPlan.Tests.ps1 - the channel-selection rules are pure
  functions and Pester pins them (no domain => no edge attempt; the PS 5.1
  one-element-unroll trap). First verification tests in the mirror.
- README: the verify block now documents viaProxy/upstream (they shipped
  in the docker-network read but were never in the README),
  timeoutSeconds, and the channel order with why it re-resolves per retry.
- README.txt: generated plain-text twin, via scripts/readme_txt.py
  (vendored from the fleet's reference implementation; the file is
  generated, never edited by hand).
- CHANGELOG.md/.txt: entries merged into the existing Unreleased sections.

CHECKSUMS.txt refreshed (42 entries). Pester: 240 passed, 0 failed.
Both synced files parse clean.

Observed, untouched: the Unreleased section carries duplicate "### Fixed"
headings from earlier appends; folding them risks reordering entries whose
prose references their neighbours, so it is left for the next release cut
(zbump #110 rolls Unreleased into the version being cut).
2026-08-31 14:44:26 -05:00

97 lines
4.3 KiB
PowerShell

# Deploy-verification planning (#101).
#
# Invoke-Pester .\tests
#
# The wrong-vhost failure was never about parsing a response - it was about
# WHICH channel got asked. So the channel-selection rules live in a pure
# function (Get-VerifyAttempts) and are pinned here, where they can be tested
# without an EC2 box: a project with no domain must never produce an edge
# attempt, because an edge request with no Host header can only reach the
# default vhost - which is a different product. That exact gap read evo.ehs's
# build number during evo-ai deploys twice on 2026-08-31 alone.
#
# ZHelpers.ps1 is dot-sourced rather than zdeploy.ps1: zdeploy executes its
# main flow on load, helpers only define functions.
BeforeAll {
. (Join-Path (Split-Path -Parent $PSScriptRoot) "ZHelpers.ps1")
function New-Proj {
param($Verify = $null, $Domain = $null, $Deploy = $null)
$p = [pscustomobject]@{}
if ($null -ne $Verify) { $p | Add-Member verify ([pscustomobject]$Verify) }
if ($null -ne $Domain) { $p | Add-Member domain $Domain }
if ($null -ne $Deploy) { $p | Add-Member deploy ([pscustomobject]$Deploy) }
return $p
}
}
Describe "Get-VerifyAttempts" {
It "puts the docker-network read first when configured" {
$proj = New-Proj -Verify @{ viaProxy = "evo_edge_proxy"; upstream = "app:80"; port = 8005 } -Domain "x.example"
$attempts = Get-VerifyAttempts -Proj $proj -ExecCmd "docker exec ..."
$attempts[0].Kind | Should -Be 'exec'
($attempts | ForEach-Object Kind) | Should -Be @('exec', 'port', 'edge')
}
It "never asks the edge for a project with no domain (the #101 trap)" {
# The shape that hit #101: viaProxy + port, no domain. The old code
# fell back to the bare IP here and read another product's counter.
$proj = New-Proj -Verify @{ viaProxy = "evo_edge_proxy"; upstream = "deploy-app-1:8000"; port = 8005; path = "/health" }
$attempts = Get-VerifyAttempts -Proj $proj -ExecCmd "docker exec ..."
($attempts | ForEach-Object Kind) | Should -Not -Contain 'edge'
}
It "returns an empty plan when nothing trustworthy exists" {
# No verify config, no domain: the caller must SKIP, not guess.
$attempts = Get-VerifyAttempts -Proj (New-Proj) -ExecCmd ""
$attempts.Count | Should -Be 0
}
It "keeps the edge for a project with a domain, with its Host header" {
$proj = New-Proj -Domain "jwks.example"
$attempts = Get-VerifyAttempts -Proj $proj -ExecCmd ""
$attempts.Count | Should -Be 1
$attempts[0].Kind | Should -Be 'edge'
$attempts[0].HostHeader | Should -Be "jwks.example"
}
It "prefers deploy.verifyHost over domain for the edge Host header" {
$proj = New-Proj -Domain "old.example" -Deploy @{ verifyHost = "new.example" }
$attempts = Get-VerifyAttempts -Proj $proj -ExecCmd ""
$attempts[0].HostHeader | Should -Be "new.example"
}
It "carries the verify path into the port attempt, defaulting sensibly" {
$proj = New-Proj -Verify @{ port = 8005; path = "/health" }
(Get-VerifyAttempts -Proj $proj -ExecCmd "")[0].Path | Should -Be "/health"
$proj2 = New-Proj -Verify @{ port = 9000 }
(Get-VerifyAttempts -Proj $proj2 -ExecCmd "")[0].Path | Should -Be "/api/build-version"
}
It "survives the PS 5.1 one-element unroll" {
# A single attempt must still come back as something with .Count and
# index access - the pipeline trap that deadlocked ztests day 2.
$proj = New-Proj -Verify @{ port = 8005 }
$attempts = Get-VerifyAttempts -Proj $proj -ExecCmd ""
$attempts.Count | Should -Be 1
$attempts[0].Kind | Should -Be 'port'
}
}
Describe "Get-VerifyTimeout" {
It "uses the caller's default when the project says nothing" {
Get-VerifyTimeout -Proj (New-Proj) -DefaultSec 30 | Should -Be 30
}
It "lets a slow-booting project widen its own window" {
# An app that runs database migrations in its entrypoint exceeds
# 30s on every deploy that ships one, and a warning that fires on
# routine success trains people to ignore the real one.
$proj = New-Proj -Verify @{ viaProxy = "p"; upstream = "u"; timeoutSeconds = 120 }
Get-VerifyTimeout -Proj $proj -DefaultSec 30 | Should -Be 120
}
}