Files
DeepSeek-Reasonix/internal/testenv
SivanCola b568386bcc Repair the Windows test job: writer leases, watch helper, stream cost / 修复 Windows 测试任务:写者租约、监听 helper、流式开销 (#10321)
The push CI `test (windows-latest)` job has failed on every completed run since
#10267 merged, and its 20-minute budget also expired. Four independent root
causes are fixed here; each was reproduced and re-verified on a real Windows 11
virtual machine by cross-compiling the affected package test binaries before and
after the change.

* fix(skill): serve the watcher helper entry from test binaries

Problem: internal/skill and internal/skill/skillwatch failed on Windows with
"timed out waiting for physical watch registration" and "physical watches = 0",
and the two packages took 90s and 70s.

Root cause: Windows always routes watching through the helper backend, which
starts by re-executing os.Executable(). Under `go test` that is the package test
binary, which never calls MaybeRunHelper, so each spawned child re-ran the whole
suite instead of serving the pipe. Registration timed out, the client burned its
restart budget, and every root degraded to scan fallback.

Fix: call MaybeRunHelper from the TestMain of the affected test binaries. Add
TestDefaultHelperCommandServesTheRealBinaryEntry, which forces the same default
command so a missing entry fails on every platform rather than only on Windows.
Also wait for registration instead of asserting it immediately, and compare
discovered catalog content rather than two stores' independent rebuild counters,
which were never required to be equal.

Verification: on Windows the baseline binary left 501 orphaned test processes
behind and failed; the fixed binary passes and leaves none. Locally the guard
reproduces the exact timeout when the entry is removed (69.5s) and passes with it
(0.5s).

* fix(session): release session writer leases at their owning boundary

Problem: about thirty tests across internal/bot, internal/serve and
internal/session failed Windows cleanup with "The process cannot access the file
because it is being used by another process" on a sessions-v4 ownership lock.

Root cause: a Service holds its writer lease until CloseAll or its idle TTL, and
ClientBinding.Release only detaches a client. Test fixtures created services and
never closed them, so the lease was still held when t.TempDir removed the
directory. POSIX unlinks open files, so the leak was invisible outside Windows.
BotGateway.Stop had the same gap in shipping code: it retired session states but
never released the per-root service cache, so a stopped gateway kept its leases
for the process lifetime.

Fix: close the service at each fixture's owning boundary, and release the cached
services from BotGateway.Stop after the last state is retired, mirroring the
existing desktop shutdown. Two tests that left a running execution bound now idle
it first, because a bound runtime refuses to close. Add
filelock.HeldPathsForTest and testenv.VerifyNoLeakedFileLocks, wired into
RunWithIsolatedUserState and the session and control TestMain, so a leaked lease
fails on every platform and names the leaking test through its temp path.

Verification: the guard reproduces the exact Windows failure set on macOS. On
Windows, internal/session went from 17 failures to none, internal/serve from 16
to none, and internal/bot from 1 to none.

* fix(control): wait for close finalization in the controller fixture

Problem: TestTurnDoneCarriesValidatedCheckpointAcrossSuccessAndError
intermittently failed cleanup with "directory not empty" under load.

Root cause: Controller.Close only starts teardown; finalizeControllerClose joins
the goal-driver workers that may still write under SessionDir. The fixture waited
on closeFinalized only when a SessionService was supplied, so other controllers
raced t.TempDir removal.

Fix: make that wait unconditional.

Verification: six concurrent suite runs reproduced the failure before the change
and were clean after it.

* test(serve): drain the message-field stream without thousands of round trips

Problem: TestCanonicalSessionMessageFieldHTTPStreamsAlignedFragments took 88.8s
on Windows CI and was the largest single contributor to the job timeout.

Root cause: it read a 144 KB body 64 bytes at a time, costing about 2250
localhost round trips. Rune- and escape-safe cutting is a property of the cut
size, not of the number of cuts.

Fix: keep narrow 64-byte reads for the first fragments so boundaries still land
inside multi-byte runes, then drain the remainder in large fragments. Every
existing assertion is retained.

Verification: on Windows the test went from failing at 10.66s to passing at
0.20s; round trips dropped from about 2250 to about 11.
2026-09-15 13:08:36 +08:00
..