Skip to content

Add SCM-launched real-binary E2E test (P0 follow-up to PR #274)

Placeholder ppxd requested to merge feat/scm-launched-e2e-test into main

Summary

  • New high-fidelity E2E R3hScm_RealBinary_LaunchedThroughSCM_ScriptDispatchRoundTripsAndStopsCleanly — verifies PR #274's UseWindowsService() production fix actually works under real SCM
  • Pre-#274: sc start would time out at 30s (ERROR_SERVICE_REQUEST_TIMEOUT) because the binary never called StartServiceCtrlDispatcher
  • Post-#274 + this test: SCM transitions START_PENDING → RUNNING → STOPPED correctly, polling channel comes up, scripts dispatch + round-trip, Stop signal cleanly drains
  • Closes the residual fidelity gap with Linux R1h (which uses systemd-launched start)

Why this is P0

PR #274 added UseWindowsService() integration to Squid.Tentacle.exe, but only had 16 unit tests covering the detection seam. The end-to-end SCM-launched path was never exercised on a real Windows runner. If PR #274's production code has a subtle bug (e.g. WindowsServiceLifetime not propagating Stop signal correctly), it would ship undetected.

This test IS the contract verification — it would have failed pre-#274 with an obvious timeout error message naming the exact production gap.

Coverage delta vs prior tests

Test SCM Polling Dispatch Stop signal
R1h-Windows (PR #271) ❌ Process.Start ✅ ✅ ❌ Process.Kill
R2h-Windows soak (PR #272) ❌ Process.Start ✅ ✅ × 5 ❌ Process.Kill
R3hScm-Windows (this PR) ✅ sc start ✅ ✅ ✅ sc stop

Mechanism (12 steps)

  1. StubSquidServer.StartAsync
  2. Pre-create instance via InstanceRegistry
  3. Real binary register --comms-url=stub.PollingUri 4-5. Extract agent thumbprint + subscriptionId, stub.TrustAgent
  4. Real binary service install — WindowsServiceHost.Install calls sc.exe create + sc.exe start
  5. Wait for SCM state == RUNNING (45s timeout — would fail fast pre-#274)
  6. Wait for polling channel via stub.ProbeCapabilitiesPollingAsync
  7. THE MAIN PIN: stub.DispatchAndObservePollingAsync PowerShell round-trip
  8. Real binary service stop — sends SCM Stop signal
  9. Wait for SCM state == STOPPED (drain + transition, ~3-15s)
  10. Cleanup: service uninstall --purge

What this catches

  • WindowsServiceLifetime integration regression — Step 7's RUNNING-state wait fails fast if the lifetime isn't wired correctly
  • TentacleEntry.ShouldRunUnderScm detection regression — if the seam returns false for SCM-launched, host bypasses the lifetime, SCM times out
  • SCM Stop signal propagation — Step 11's STOPPED-state wait fails if host CT cancellation doesn't reach the polling drain logic (operator's sc stop would hang)
  • TentacleScmHostedService.LastExitCode propagation — SCM records exit codes; if the hosted service swallows them, ops lose post-mortem signal

Test plan

  • dotnet build tests/Squid.WindowsTentacleE2ETests — 0 errors
  • CI on windows-latest runs Category=WindowsTentacleBinaryE2E:
    • R3hScm_RealBinary_LaunchedThroughSCM_ScriptDispatchRoundTripsAndStopsCleanly passes
    • All other R1h/R2h/smoke tests still green
  • Re-run the workflow 2-3 times to confirm SCM transitions are deterministic

Phase status

This is P0-#1 of the post-1.6.4-release P0/P1 hardening sweep. Sister PRs:

  • P0-#3 (#275): LocalScriptService async-flush race fix — eliminates timing flakes at the root
  • P0-#2 (next): Server-restart polling reconnect E2E

🤖 Generated with Claude Code

Merge request reports

Loading