test: wait for the retired tunnel start's cleanup before checking it
CloudTunnelLaunchGateTests.optOutDuringInstallRemovesLateSavedConfiguration no longer checks the tunnel's cleanup before that cleanup has run. It failed once in the #15488 validation run with controller.calls == ["install", "stop"] where ["install", "stop", "remove"] was expected. The test now waits for the cleanup's last step before it asserts anything.
Cause
The test starts a tunnel whose install waits for approval, turns Cloud Machines off, and then approves late:
requestDown()retires the start that is waiting on approval and publishes.off.startwaits on the state stream, so it throws as soon as.offis published. The retired start hasn't been resumed yet at that point.controller.approve()resumes the retired start. On the coordinator's actor it callsdiscard(install:), which always runscontroller.remove()and thenenroller.discardEnrollment().
The test asserted right after start threw, which races step 3. In shard 4 of run 36713362165, controller.calls had no remove at line 371, yet discardCount == 1 held two lines later. Since remove always comes first, the cleanup ran between those two expectations. No tunnel code, test or fake changed since main's last green full suite, where the test passed.
Change
FakeTunnelEnrollergets adiscardedsignal that resolves at the first discard that actually removes an enrollment.- The test waits for that signal before asserting, and
.timeLimit(.minutes(1))bounds the wait. - Every assertion is unchanged.
The second half of the test, cmux vpn down with the opt-in still on, has the mirror-image race. There it can only produce a false pass, so it is left alone.
Verification
Both proof runs use app-host-test-rerun.yml with 3 iterations. For the proof only, each one adds a 300 ms delay to the fake controller's remove(), which widens the race without changing the order.
- Red, test unchanged: run 36725360565 failed every iteration at lines 371–373:
controller.callshad noremove, the configuration was still installed anddiscardCountwas 0. The cleanup simply hadn't run yet. - Green, with this change: run 36725366119 passed every iteration with the same delay.
Changelog
- none
🤖 Generated with Claude Code