Test gaps 4-6: relay unit tests, updater-refusal, password walk-through

Closing the coverage gaps the review flagged as blind spots we'd be relying on at
release:

4. RELAY LOGIC TESTS. server/test_relay.py (stdlib unittest + a FakeSocket, no network)
   covers the address-proof end to end: cookie issued on join, wrong cookie rejected,
   right cookie verifies once; enforce mode WITHHOLDS forwarding from an unverified
   address then delivers after it proves itself; watch-only forwards but records
   would-block; the per-IP cap counts across BOTH v1 and v2; a NAT-rebind clears
   verification (spoof-takeover guard); a forged BYE from another address can't evict
   the victim; and bad/short/unknown-version headers are refused. Wired into
   run-tests.ps1 (Start-Process from server\, SKIPs loudly if no Python) so a relay
   change can no longer ship past the gate untested. The relay had ZERO automated
   coverage before and auto-updates every user.

5. UPDATER SIGNATURE ENFORCEMENT. Extracted the two refusal branches into a pure
   VerifyStagedRelease gate and added UpdaterRefusesUnsignedRelease: no-sig refused,
   wrong-key refused, garbage refused, tamper (good sig over changed bytes) refused,
   genuine release accepted. ReleaseSigning only proved the crypto; this proves the
   updater actually REFUSES - the hijacked-release-stream threat.

6. STREAMING PASSWORD STRENGTHENING. The accept decision is now a pure
   ProfilePasswordDialog.RejectionAdviceFor shared by BOTH password dialogs (also
   fixes the App-review trim inconsistency - manager dialog compared untrimmed). Test
   pins the load-bearing rule: requireStrong DISABLES the unchanged-exemption so an
   existing weak "Games" can't keep streaming, while casual mode still grandfathers an
   unchanged password and blocks a new weak one, trim-safe.

Plus the NVDA-hang cache assertions in PasswordRules (miss->hit, same-instance repeat,
Prewarm, empty/weak = no work).

Gate 71/71 + 7 relay tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-27 10:11:52 +01:00
co-authored by Claude Fable 5
parent 1b6402f786
commit 65e999466d
8 changed files with 336 additions and 30 deletions
+34
View File
@@ -93,6 +93,40 @@ if ($packet -match 'DefaultPort\s*=\s*(\d+)') { if ($Matches[1] -ne '47830') { F
if ($relay -notmatch '47830') { Fail "relay no longer references port 47830"; $wireOk = $false }
if ($wireOk) { Pass "relay magic / version / port still match the client header - no server change needed" }
# Relay logic unit tests (server\test_relay.py). The relay's address-proof, per-IP cap, NAT-rebind
# reset and forged-BYE rejection are pure Python guarding an internet-facing attack surface, and the
# relay auto-updates every user - a regression there would sail past the C# gate. Run them here so
# a server change can't ship un-tested. Needs a Python interpreter; if none is found we SKIP loudly
# rather than fail (the C# gate doesn't depend on Python being installed on the build box).
Write-Host "`nRelay logic tests (server\test_relay.py):" -ForegroundColor Cyan
$py = $null
foreach ($cand in @('py', 'python', 'python3')) {
$cmd = Get-Command $cand -ErrorAction SilentlyContinue
if ($cmd) { $py = $cmd.Source; break }
}
if (-not $py) {
Write-Host " [SKIP] no Python interpreter found (py/python/python3) - relay logic tests did not run" -ForegroundColor Yellow
Write-Host " WARNING: the relay's address-proof / cap / eviction logic is NOT verified on this machine." -ForegroundColor Yellow
}
else {
# Start-Process (not the call operator) so unittest's stderr can't trip $ErrorActionPreference=Stop,
# and so it runs FROM server\ where the test's relative import of remsound-relay.py resolves.
$serverDir = Join-Path $repo 'server'
$rtOut = Join-Path $env:TEMP ("rs-relay-" + [guid]::NewGuid().ToString('N') + ".txt")
$rtErr = Join-Path $env:TEMP ("rs-relay-" + [guid]::NewGuid().ToString('N') + ".err.txt")
$rp = Start-Process -FilePath $py -ArgumentList @('-m', 'unittest', 'test_relay') -WorkingDirectory $serverDir `
-Wait -NoNewWindow -PassThru -RedirectStandardOutput $rtOut -RedirectStandardError $rtErr
$rtText = ((Get-Content -LiteralPath $rtOut -Raw -ErrorAction SilentlyContinue) + "`n" + (Get-Content -LiteralPath $rtErr -Raw -ErrorAction SilentlyContinue))
Remove-Item $rtOut, $rtErr -Force -ErrorAction SilentlyContinue
if ($rp.ExitCode -eq 0) {
$ran = if ($rtText -match 'Ran (\d+) test') { $Matches[1] } else { '?' }
Pass "relay logic tests passed ($ran tests: addr-proof, enforce/watch, IP cap, rebind, forged-BYE, header gate)"
}
else {
Fail "relay logic tests FAILED:`n$rtText"
}
}
# ---- 4. CLI SURFACE + IN-APP SELF-TEST (these launch the app, which consolidates sounds away;
# that's why the package checks ran first) ----
function Invoke-RsCli([string[]]$cliArgs) {