Skip to content

test(tui): isolate user home, config and cache dirs for the TUI tests - #1114

Open
PierrunoYT wants to merge 1 commit into
Twigpine:mainfrom
PierrunoYT:test/tui-hermetic-user-dirs
Open

PierrunoYT wants to merge 1 commit into
Twigpine:mainfrom
PierrunoYT:test/tui-hermetic-user-dirs

Conversation

@PierrunoYT

Copy link
Copy Markdown
Contributor

Summary

First, deliberately scoped step of #1103 (test suite is not hermetic). newModel loads <user config>/zero/commands/*.md through config.UserConfigDir(), and internal/tui has 500+ newModel( calls with no isolation, so a developer's own slash commands and state leak into the TUI tests, and a test could write to the real user directories. That is the "Hermetic tests" rule in AGENTS.md.

This PR:

  • adds testutil.IsolateUserDirs(root), which points HOME, USERPROFILE, APPDATA, LOCALAPPDATA and the XDG_* roots at a temp dir and returns a restore function. All of them are set, since os.UserConfigDir/os.UserCacheDir read the XDG variables only on Linux/BSD, derive from HOME on macOS, and from %AppData%/%LocalAppData% on Windows;
  • calls it from a new TestMain in internal/tui. Tests that set their own layout with t.Setenv still win while it is active.

Production code is not touched.

Not included, on purpose (AGENTS.md asks that broad changes be discussed first): the ZERO_CONFIG_DIR override in config, routing the ~26 non-test callers of os.User{Config,Home,Cache}Dir through it, and TestMain in the other packages. The helper added here is what those follow-ups would call, so this issue should stay open after this PR.

Linked issue

Refs #1103 (partial, does not close it).

Note: #1103 does not currently carry the issue-approved label. Opening this anyway at the author's request; it can be held until the issue is approved.

Verification

  • TestUserDirsAreIsolatedFromTheDeveloperHome checks that the home, config and cache dirs resolve inside the isolated root. Without the TestMain it fails (home dir "C:\Users\<dev>" is outside the isolated root "", same for config and cache); with it, it passes.
  • TestNewModelLoadsUserCommandsOnlyFromIsolatedConfig checks a fresh model loads no user commands, and that a command planted in the isolated config dir is the only one it sees.
  • TestIsolateUserDirsRedirectsAndRestores (in internal/testutil) checks the redirection through os.UserHomeDir/UserConfigDir/UserCacheDir and that previous values, including unset ones, are restored.
  • The full go test ./internal/tui/ suite passes with the directories redirected (45 s locally on Windows).
  • go build ./..., go vet ./internal/tui/ ./internal/testutil/, gofmt -l, git diff --cached --check clean.
  • Only run on Windows. Linux/macOS behavior is by construction (all variables set) and left to CI; -race, full go test ./..., smoke and make targets were not run locally.

Checklist

  • The linked issue already has the issue-approved label. (not yet)
  • go build ./..., go vet ./..., and go test ./... pass locally. (build passes; vet and tests only for the affected packages)
  • gofmt clean.
  • Tests added/updated for the change (and run under -race where relevant). (tests added; -race not run locally)
  • UI changes include screenshots or a short recording where possible. (no UI change)

🤖 Generated with Claude Code

newModel reads <user config>/zero/commands/*.md, and the package has 500+
newModel calls with no isolation, so a developer's own slash commands and
state leaked into the TUI tests (and tests could write to real user
directories), against the AGENTS.md "Hermetic tests" rule.

Add testutil.IsolateUserDirs, which points HOME, USERPROFILE, APPDATA,
LOCALAPPDATA and the XDG_* roots at a temp dir (covering Linux, macOS and
Windows), and call it from a TestMain in internal/tui. Per-test t.Setenv
overrides still win.

Refs Twigpine#1103

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 17:57
@coderabbitai

coderabbitai Bot commented Oct 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Only developers with an assigned seat can use this organization's usage-based review budget, and seats here are assigned manually. Ask an admin to assign a seat, or change the review continuation mode in Billing.

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: Twigpine/zero/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c7fde088-0934-4514-9121-ee95255b2805

📥 Commits

Reviewing files that changed from the base of the PR and between 99721c7 and 0116797.

📒 Files selected for processing (4)
  • internal/testutil/userdirs.go
  • internal/testutil/userdirs_test.go
  • internal/tui/hermetic_test.go
  • internal/tui/main_test.go
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Environment and filesystem cleanup failures can be silently ignored, undermining the isolation guarantee.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds package-wide user-directory isolation for TUI tests to prevent access to real user state.

Changes:

  • Adds reusable environment isolation and restoration.
  • Configures TUI TestMain with temporary user directories.
  • Adds regression tests for directory and command isolation.
File Description
internal/​tui/​main_test.go Establishes package-wide isolation.
internal/​tui/​hermetic_test.go Verifies TUI isolation behavior.
internal/​testutil/​userdirs.go Implements the isolation helper.
internal/​testutil/​userdirs_test.go Tests redirection and restoration.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

for _, name := range userDirEnv {
value, set := os.LookupEnv(name)
previous[name] = saved{value: value, set: set}
_ = os.Setenv(name, root)
// helper hands them back (set ones restored, unset ones unset again).
t.Setenv("HOME", "before-home")
t.Setenv("XDG_STATE_HOME", "before-state")
os.Unsetenv("XDG_DATA_HOME")
Comment thread internal/tui/main_test.go
restore := testutil.IsolateUserDirs(root)
code := m.Run()
restore()
_ = os.RemoveAll(root)

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants