fix(pi): isolate the test suite from the host config directory - #219
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Architecture diagram
sequenceDiagram
participant Bun as Bun Test Runner
participant Preload as Pi Test Preload (setup.ts)
participant TestSuite as Pi Test Suite
participant Core as Core Modules (accounts.ts, paths.ts)
participant Env as Process Env Vars
participant FS as Temp Filesystem
Note over Bun,FS: Current State: Isolated Pi Test Runtime
Bun->>Preload: Load preload (bunfig.toml)
Preload->>FS: mkdtemp() per-test temp dir
Preload->>Env: Capture host OPENCODE_CONFIG_DIR, OPENCODE_ANTHROPIC_AUTH_FILE,<br/>OPENCODE_ANTHROPIC_AUTH_STATE_FILE, PI_AGENT_DIR,<br/>PI_ANTHROPIC_AUTH_* (store originals)
loop Each Test Case
Preload->>Env: Redirect all captured vars into test temp dir<br/>(pin OPENCODE_* + PI_AGENT_DIR, reset PI_ANTHROPIC_AUTH_*)
Env-->>TestSuite: Clean isolated config paths
TestSuite->>TestSuite: Run test (e.g. stream.test.ts)
alt Test reads config paths
TestSuite->>Core: getConfigDir() / auth file resolution
Core->>Env: Read OPENCODE_CONFIG_DIR (pinned to temp)
Env-->>Core: Temp path (not host)
Core-->>TestSuite: Isolated config data (or missing file → controlled failure)
alt PI_ANTHROPIC_AUTH_* state vars used
TestSuite->>Env: Set individual state vars per test
Env-->>TestSuite: Test-specific state
end
end
TestSuite-->>Preload: Test complete
Preload->>Env: Restore original host env vars<br/>(delete if absent, else restore saved value)
Preload->>FS: rm temp dir (cleanup)
end
Note over Preload: Restoring originals ensures host env untouched<br/>after suite exits (no residual pollution).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
`packages/pi` had no test preload, so its suite inherited the host's config directories. One test read the operator's live `~/.config/opencode/anthropic-auth.json` and passed because of what happened to be in it — on a machine without that file it failed, which is CI and anyone who clones the repo. Varying one variable at a time against the failing test on a developer machine: unsetting `HOME`, `XDG_CONFIG_HOME`, or `OPENCODE_CONFIG_DIR` each turned it red, while `PI_AGENT_DIR` did not. Pi's own agent dir is not the dependency; `getConfigDir()` in `packages/core/src/accounts.ts` is, and Pi tests reach core code that falls back to it. Pre-existing rather than caused by a recent change — a clean detached worktree at `main` fails identically under an empty `HOME`. `packages/core` and `packages/opencode` each received this preload on 2026-09-07; `packages/pi` was missed. The preload deliberately does not restore the host's original values. Restoring them in `afterEach` re-exposed host config mid-run: Bun runs `afterEach` inner-file-first and the preload's hook last, so a per-file `afterAll` saw them again — and `commands.test.ts` asserts `OPENCODE_ANTHROPIC_AUTH_STATE_FILE` is unset. Restoring is also unnecessary, since this is in-process mutation that cannot reach the operator's shell and every `beforeEach` re-isolates.
fdd33ec to
9f59e05
Compare
|
Stack, for merge order. Four PRs came out of chasing three separate CI failures to root cause; each fix is in its own PR rather than folded into the custody change. Suggested order: #219 → #228 → #229 → #218. Each rebases cleanly on the previous. This PR: base of the stack. Its own check is red on The three CI failures were unrelated to each other: a Pi suite reading the operator's live config (#219), manifest-lock tests racing a wall-clock TTL (#228), and a genuine product bug where the model-restored notice never reaches a session with no TUI attached (#229). |
packages/pihas no test preload, so its suite inherits the host's config directories. One test reads the operator's live~/.config/opencode/anthropic-auth.jsonand passes because of what happens to be in it — on a machine without that file it fails.That is CI, and anyone who clones the repo.
Evidence
packages/pi/src/tests/stream.test.ts→sticky-balanced preserves the strict API fallback gate after OAuth exhaustion, varying one variable at a time on a developer machine:Pi's own agent dir is not the dependency. The OpenCode config dir is:
getConfigDir()inpackages/core/src/accounts.tsresolves$OPENCODE_CONFIG_DIR, else$XDG_CONFIG_HOME/opencode, else~/.config/opencode, and Pi tests reach core code that falls back to it.Pre-existing, and not caused by any recent change — a clean detached worktree at
mainfails identically under an emptyHOME.packages/coreandpackages/opencodeeach received this preload on 2026-09-07;packages/piwas missed.Change
packages/pi/bunfig.toml+packages/pi/src/tests/setup.ts, mirroring the existing two setups: a per-testmkdtemp, host path variables deleted and redirected into it, temp dir removed.Seven variables, chosen by reading the call graph rather than by guessing —
OPENCODE_CONFIG_DIR,OPENCODE_ANTHROPIC_AUTH_FILE,OPENCODE_ANTHROPIC_AUTH_STATE_FILE(accounts.ts:514-528),PI_AGENT_DIR(paths.ts:7), and the threePI_ANTHROPIC_AUTH_*state vars (stream.ts:138,157) which are reset rather than pinned since tests set them individually.The preload deliberately does not restore the host's original values. The first version did, in
afterEach— but Bun runsafterEachinner-file-first and the preload's hook last, so a per-fileafterAllsaw host values again, andcommands.test.ts:82assertsOPENCODE_ANTHROPIC_AUTH_STATE_FILEis unset. Any host exporting that variable got a red suite from the isolation meant to make it deterministic:Restoring is unnecessary anyway: this is in-process mutation that cannot reach the operator's shell, and every
beforeEachre-isolates. Dropping the snapshot removed a second hazard with it — its entries were only cleared inafterEach, so a test that threw could leave a stale value to restore over a later test's isolation.Verification
Empty env means
HOME,XDG_CONFIG_HOME,OPENCODE_CONFIG_DIRandPI_AGENT_DIRall pointed at a fresh temp dir. The before/after pair is what shows the preload is load-bearing rather than incidental.core 188/188 · opencode 1869/1869 · pi 114/114 · typecheck, format, biome clean.
No production file and no test assertion was modified.
Note
OPENCODE_CONFIG_DIRalone was not sufficient in practice — the storage and state file variables had to be pinned too. The isolation is proven by the suite passing in a clean environment; the precise call site that still reached the host path was not pinned down by reading, and is recorded here rather than left implied.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Isolates the
packages/pitest suite from the host's config directories so tests pass in CI and on machines without a live OpenCode config.packages/coreandpackages/opencode;packages/piwas missed when they received theirs.Written for commit 9f59e05. Summary will update on new commits.