Skip to content

7 bats tests fail on macOS due to unresolved /var -> /private/var symlink in test-built expected paths #536

Open

Description

@wz-gsa

What happened

Running the full offline suite (./scripts/test-acq-bats, no file argument — 545 tests) on a real macOS host surfaces 7 failures, all sharing the identical root cause:

not ok 119 clone(msb): --clone mounts a scratch clone at the original path and registers the sandbox remote
not ok 128 clone(msb): secondary workspaces keep their direct mounts
not ok 157 msys: a mount uses the host form for SOURCE and the guest form for TARGET
not ok 158 msys: recorded github-scope workspace uses the host form
not ok 223 github-scope: uses recorded sandbox workspace when path is omitted
not ok 370 msb: provision binds USAi + GitHub via --secret with --tls-intercept, no value leak
not ok 391 msb: multi-workspace mounts each at its host path; start dir is the primary

All 538 other tests pass. This is a test-construction fragility, not a defect in acq itself — flagging per the "Track Deferred Work" rule in AGENTS.md rather than leaving it silently observed.

Root cause

On macOS, /var and /tmp are symlinks to /private/var and /private/tmp (readlink /var → private/var). scripts/test-acq-lib.sh's acq_setup_stubs builds $STUBDIR directly from the raw environment:

STUBDIR=$(mktemp -d "${TMPDIR:-/tmp}/acq-test.XXXXXX")

$TMPDIR on macOS is /var/folders/.../T/ — the unresolved symlink form, with a trailing slash. Several @test blocks then build their OWN expected-value regex by hand, directly against $STUBDIR or $BATS_TEST_TMPDIR (which inherits the same unresolved form), e.g. (test/bats/116-clone-option.bats):

assert_regex "$line" "--volume $(host_path "$scratch"):${repo}( |\$)"

But the ACTUAL value acq emits goes through its own path-canonicalization logic (host_path/canonicalize_path in acq.backends/common.sh, ADR-0029) somewhere along the way, which resolves the symlink to its physical target. So the real argv has /private/var/folders/.../T/acq-test.XXXXXX/... (resolved, single slash), while the test's hand-built expectation has /var/folders/.../T//acq-test.XXXXXX/... (unresolved, doubled slash from the trailing / + the literal / the test code appends).

Confirmed directly:

PATTERN: --volume /var/folders/ts/.../T//acq-test.X1zUbF/state/clones/.../cloneproj:...
VALUE  : --volume /private/var/folders/ts/.../T/acq-test.X1zUbF/state/clones/.../cloneproj:...

Two distinct symptoms, same cause (the test's expected string is built from the raw, unresolved $TMPDIR rather than through the same canonicalization acq applies to the real value):

  1. Missing /private prefix in the expectation.
  2. A doubled // in the expectation, from $TMPDIR's own trailing slash plus the test's own /-prefixed concatenation.

Why this doesn't show up in CI

Linux's /tmp is not a symlink and typically has no trailing slash in $TMPDIR (often unset, defaulting to /tmp), so this specific macOS-only path-resolution mismatch never triggers there. This is presumably why it's gone unnoticed — the suite is green in CI but red on a real macOS development machine.

Suggested fix

Resolve $STUBDIR's real/physical path once, at acq_setup_stubs time, e.g.:

STUBDIR=$(mktemp -d "${TMPDIR:-/tmp}/acq-test.XXXXXX")
STUBDIR=$(cd "$STUBDIR" && pwd -P)

so every test's hand-built expectation and acq's own canonicalized output start from the same resolved base path, with no further per-test workaround needed. Have not implemented this — filing for someone with more context on the test harness's design intent (and to confirm this fix doesn't have knock-on effects on the Windows/MSYS path-forms tests, which deliberately exercise host-vs-guest path translation and may have their own reasons to care about the raw vs. resolved distinction).

Reproduction

./scripts/test-acq-bats   # macOS, real $TMPDIR with a /var symlink

7/545 fail with the signature above; 538/545 pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions