Make cargo test --all pass on macOS #269

Merged
stephen merged 1 commit from fix/hermetic-config-tests into main 2026-09-11 08:59:31 +00:00
Owner

cargo test --all could not finish on a Mac. Every failure was a test reading the developer's real machine instead of its own scratch state.

ProjectDirs ignores XDG_CONFIG_HOME on macOS and resolves to ~/Library/Application Support, so the host-resolution tests and the SIGINT backstop tests were loading the real hosts.toml. When that named a host whose token lives in the login keychain, the lookup then blocked forever inside securityd: a keychain item's ACL names the binaries allowed to read it, and a freshly built test binary is not one of them, so the read hangs instead of failing. Diagnosing it needs sample <pid>, because from the outside it just looks like a test that never returns.

FJ_CONFIG_DIR names the config directory outright and FJ_NO_KEYCHAIN keeps token reads, writes and deletes in the 0600 file store, which is where those tests had put their tokens anyway. Both are useful to any scripted run that wants its own state, so they are in the README table rather than hidden behind cfg(test).

Two more, found once the suite could get that far:

  • The editor test hardcoded /bin/false, which exists on Linux but not on macOS, so it asserted on a spawn failure rather than the nonzero exit it was written for. It now writes its own failing script, the way the sibling test already writes its own editor.
  • The loopback sign-in test drove real sockets on a paused clock, which cannot work: tokio's auto-advance does not wait for socket readiness, so it jumped the clock past the five-minute callback deadline while the silent connection was still in the accept queue. It now runs on the real clock with the request-line bound shortened through a test-only override, so it still proves the bound fires and still costs milliseconds. The shipped ten-second value is untouched.

No production behaviour changes beyond the two new escape hatches, both of which default to today's resolution when unset.

Verified locally: cargo fmt --check clean, cargo clippy --all-targets --all-features -- -D warnings clean, cargo test --all green (794 + 5 + 1, exit 0) on macOS 26.

`cargo test --all` could not finish on a Mac. Every failure was a test reading the developer's real machine instead of its own scratch state. `ProjectDirs` ignores `XDG_CONFIG_HOME` on macOS and resolves to `~/Library/Application Support`, so the host-resolution tests and the SIGINT backstop tests were loading the real `hosts.toml`. When that named a host whose token lives in the login keychain, the lookup then blocked forever inside securityd: a keychain item's ACL names the binaries allowed to read it, and a freshly built test binary is not one of them, so the read hangs instead of failing. Diagnosing it needs `sample <pid>`, because from the outside it just looks like a test that never returns. `FJ_CONFIG_DIR` names the config directory outright and `FJ_NO_KEYCHAIN` keeps token reads, writes and deletes in the 0600 file store, which is where those tests had put their tokens anyway. Both are useful to any scripted run that wants its own state, so they are in the README table rather than hidden behind `cfg(test)`. Two more, found once the suite could get that far: - The editor test hardcoded `/bin/false`, which exists on Linux but not on macOS, so it asserted on a spawn failure rather than the nonzero exit it was written for. It now writes its own failing script, the way the sibling test already writes its own editor. - The loopback sign-in test drove real sockets on a paused clock, which cannot work: tokio's auto-advance does not wait for socket readiness, so it jumped the clock past the five-minute callback deadline while the silent connection was still in the accept queue. It now runs on the real clock with the request-line bound shortened through a test-only override, so it still proves the bound fires and still costs milliseconds. The shipped ten-second value is untouched. No production behaviour changes beyond the two new escape hatches, both of which default to today's resolution when unset. Verified locally: `cargo fmt --check` clean, `cargo clippy --all-targets --all-features -- -D warnings` clean, `cargo test --all` green (794 + 5 + 1, exit 0) on macOS 26.
Make cargo test --all pass on macOS
All checks were successful
ci / check (pull_request) Successful in 11m48s
ci / live-e2e (pull_request) Successful in 2m11s
ci / coverage (pull_request) Successful in 2m57s
83f8e6e47a
The suite could not finish on a Mac, and every failure was a test reading
the developer's real machine instead of its own scratch state.

`ProjectDirs` ignores `XDG_CONFIG_HOME` on macOS and resolves to
`~/Library/Application Support`, so the host-resolution tests and the
SIGINT backstop tests were loading the real `hosts.toml`. When that named
a host whose token lives in the login keychain, the lookup then blocked
forever inside securityd: a keychain item's ACL names the binaries
allowed to read it, and a freshly built test binary is not one of them,
so the read hangs rather than failing. `FJ_CONFIG_DIR` names the config
directory outright and `FJ_NO_KEYCHAIN` keeps token reads and writes in
the file store, which is what those tests had populated anyway. Both are
equally useful for a scripted run that wants its own state, so they are
documented rather than test-only.

The editor test hardcoded `/bin/false`, which exists on Linux but not on
macOS, so it asserted on a spawn failure instead of the nonzero exit it
was written for. It now writes its own failing script, the way the
sibling test already wrote its own editor.

The loopback sign-in test drove real sockets on a paused clock, which
cannot work: tokio's auto-advance does not wait for socket readiness, so
it jumped the clock past the five-minute callback deadline while the
silent connection was still sitting in the accept queue. It now runs on
the real clock with the request-line bound shortened through a test-only
override, so it still proves the bound fires and still costs
milliseconds. The shipped ten-second value is untouched.
Sign in to join this conversation.
No description provided.