Skip to content

fix(test): stop two gates depending on the host and the process interleaving - #2993

Merged
thymikee merged 3 commits into
mainfrom
fix/test-isolation-flakes
Sep 26, 2026
Merged

thymikee merged 3 commits into
mainfrom
fix/test-isolation-flakes

Conversation

@thymikee

@thymikee thymikee commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Two test-isolation defects where results depended on the machine or the process interleaving, both flagged during #2986.

check:tmpdir-leaks:test (the Repo Guards red). Four files run in one node --test invocation, parallelized per file by default. Two sweep the real shared root: node-test-tmpdir.test.ts asserts pruneAbandonedRunDirectories(TEST_RUN_TMP_ROOT) both keeps and removes a planted directory, and vitest-tmpdir-global-setup.test.ts drives a real vitest whose globalSetup sweeps that same root. One file's sweep can delete the directory the other is mid-flight on, so it observes []. It passed 5/5 locally and failed on CI with exactly that actual: []. Fix: forward --test-concurrency=1 (+2s: 3.4s → 5.5s), pinned by a test requiring the flag among the forwarded args — it spawns node <forwarded…> --test, so a flag before the wrapper path is parsed by the wrapper and leaves the lane silently parallel.

agent-browser-lifecycle.test.ts (red on any Mac with a simulator runtime). It recorded PID 101 and asserted the reaper cleared it. PID 101 is appleaccountd there, so the reaper reads ownership-lost and retains the record: green on CI, red locally. Fix: the NEVER_A_PID idiom the tmpdir lanes already use, which is also the branch the test means to exercise.

The iOS smoke flake is not addressed here; see the #2986 thread for why it needs the Mac lane.

Validation

9b9d184. check:affected --run passed; test:unit 11396 passed 0 failed (that file failed here before the fix); check:tmpdir-leaks:test 25/25; check:gate-manifest:test, depgraph:test, check:layering, check:quick exit 0. The pin fails on the previous package.json and on the misplaced-flag form.

…ory root

`check:tmpdir-leaks:test` runs four files in one `node --test` invocation, which
parallelizes per file by default. Two of them sweep the real shared root:
node-test-tmpdir.test.ts asserts `pruneAbandonedRunDirectories(TEST_RUN_TMP_ROOT)`
both keeps and removes a planted directory, and vitest-tmpdir-global-setup.test.ts
drives a real `vitest` whose global setup performs that same sweep. One file's
sweep can therefore delete the run directory the other is mid-flight on, and the
orphan test's "the next run prunes it" assertion observes `[]` instead of the
directory. It passed 5/5 locally and failed on a CI runner with exactly that
`actual: []`; the outcome depends on process interleaving, not the code.

Forward `--test-concurrency=1` (cost measured at +2s: 3.4s -> 5.5s) and pin it.
The pin checks the flag sits among the args the wrapper forwards to its child,
not merely anywhere in the command: the wrapper spawns `node <forwarded...>
--test`, so a flag written before the wrapper path is parsed by the wrapper's
own node process and never reaches the runner, leaving the lane silently
parallel. Verified that misplaced form fails the new test.
…wner

The orphan-cleanup test recorded PID 101 and asserted the reaper cleared the
record. 101 is only reliably dead on a fresh CI runner: on a Mac with a
simulator runtime mounted it is `appleaccountd`, so the reaper inspects a live
process, reads it as ownership-lost, retains the record rather than clearing it,
and the test fails on that machine while staying green on CI.

Use the `NEVER_A_PID` idiom the tmpdir lanes already use — INT32_MAX is outside
every platform's pid range, so the owner is dead by construction on every host
and cannot be recycled mid-test. That is also the branch the test means to
exercise: a recorded owner that already exited, hence a stale record that must
be cleared.
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-26 14:17 UTC

@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.85 MB 4.85 MB -158 B
Package (unpacked) 4.85 MB 4.85 MB -158 B
Package (download) 1.45 MB 1.45 MB -58 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 16.9 ms 17.6 ms +0.7 ms
CLI --help 47.3 ms 48.3 ms +1.0 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread scripts/node-test-tmpdir.test.ts
Comment thread packages/platform-web/src/agent-browser-lifecycle.test.ts Outdated
…string

Review found the pin's `includes('--test-concurrency=1')` accepted two shapes
that still run files in parallel: `--test-concurrency=10`, and the flag sitting
in a later `&&` segment of a command whose sweep segment never serializes. Split
the command the way the wrapper ratchet above already does, read the tokens the
wrapper forwards to its child, and require exactly one that equals `--test-concurrency=1`.
A wrapper path that is not a standalone token reports the lane unsynchronized
rather than guessing.

Mutations verified to fail the pin: concurrency `10`, flag removed, flag moved
before the wrapper path, a `--test-concurrency=1` decoy in a later segment, a
duplicated flag, and a `./`-prefixed wrapper path.

Soften the NEVER_A_PID comment to the claim the code can support. Windows pids
are 32-bit, so INT32_MAX does not sit outside every platform's pid range by
construction; it sits above any pid a real host mints in practice, which is what
makes the recorded owner absent here.
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed at 338e807. I found no blocking issues. All 22 checks pass, and the change is test and tooling only. I checked the serialization with an equivalent standalone repro and ran the new regression predicate against the old script line and six mutated forms. I did not run the gate against this PR's package.json, and I did not check the softened INT32_MAX claim on a win32 host.

Not blocking: the comment near https://github.com/callstack/agent-device/blob/338e807/scripts/node-test-tmpdir.test.ts#L259 still makes the old universal INT32_MAX claim that this PR corrects in agent-browser-lifecycle.test.ts. Could a follow-up reword it to match?

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 26, 2026
@thymikee
thymikee merged commit 3660c7f into main Sep 26, 2026
22 checks passed
@thymikee
thymikee deleted the fix/test-isolation-flakes branch September 26, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant