testenv

package
v0.179.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package testenv isolates a test process from the ambient machine state documented in internal/envguard: WB_AGENT_* variables inherited from whichever agent is operating the shell that launched `go test`, and an ambient GOWORK the test process would otherwise carry into every `go` invocation it makes.

WB's own test suite runs as a subprocess of that operating agent. A test that asserts "no active owner" or "no live registered session" observes the outer agent's identity instead of a clean slate unless it calls Isolate first: WB_AGENT_PID/WB_AGENT_RUNTIME/WB_AGENT_MODEL/WB_AGENT_ID are inherited by the `go test` binary itself, not only by subprocesses wb spawns, so internal/envguard's subprocess-environment sanitizing cannot reach them.

Index

Constants

View Source
const UserStateRootEnv = "WB_TEST_USER_ROOT"

UserStateRootEnv names the private root in the environment of every process an isolated test binary starts, so a re-executed test binary adopts its parent's isolation instead of building its own.

Variables

This section is empty.

Functions

func ConfigureGitAutoMaintenanceOff added in v0.164.0

func ConfigureGitAutoMaintenanceOff(t testing.TB, repoPath string)

ConfigureGitAutoMaintenanceOff runs `git config` inside repoPath to disable gc.auto, maintenance.auto and receive.autogc directly in that repository's own config file.

This is REQUIRED, not merely redundant, for any bare repository a test fixture pushes to over a local transport: git strips every GIT_CONFIG_* variable from the environment it hands to the server-side `receive-pack` it spawns for that push, so SetGitAutoMaintenanceOff/GitAutoMaintenanceOffEnv alone never reaches it. #711's review traced this directly: a bare origin.git configured only through the env still started `gc --auto` 34 times from inside receive-pack, while every directly-started git subprocess (the clones) dropped to 0.

func GitAutoMaintenanceOffEnv added in v0.164.0

func GitAutoMaintenanceOffEnv(base []string) []string

GitAutoMaintenanceOffEnv returns base with GIT_CONFIG_COUNT/KEY_N/VALUE_N entries appended that disable gc.auto, maintenance.auto and receive.autogc for any git subprocess started with the returned environment. If base already carries a GIT_CONFIG_COUNT sequence (for example a caller's own repo-identity overrides), the three keys are appended after the existing ones and GIT_CONFIG_COUNT is raised to match, rather than overwriting or colliding with them.

This only reaches a git subprocess started directly with this environment. It does not reach a server-side `receive-pack` a same-host `git push` spawns as its own child process: git strips every GIT_CONFIG_* variable from the environment it hands to that child (#711's review traced this directly). A bare remote a test pushes to must also be configured with ConfigureGitAutoMaintenanceOff on the repository itself.

func GitAutoMaintenanceOffProcess added in v0.164.2

func GitAutoMaintenanceOffProcess()

GitAutoMaintenanceOffProcess performs SetGitAutoMaintenanceOff's work for a caller with no *testing.T -- a package's TestMain -- so that every git subprocess the test binary starts inherits gc.auto, maintenance.auto and receive.autogc disabled. That includes git started by the production code under test (a pull, fetch or commit against a fixture clone, or a clone or temporary worktree production creates itself), which no per-command GitAutoMaintenanceOffEnv reaches and no ConfigureGitAutoMaintenanceOff call can name in advance. Like IsolateProcess this is not restored: TestMain's process exits once m.Run() returns. A bare remote pushed to over a local transport still needs ConfigureGitAutoMaintenanceOff (see its doc comment).

func InitBareRemoteForTest added in v0.169.0

func InitBareRemoteForTest(t testing.TB, path string) string

InitBareRemoteForTest creates a bare repository at path (with an initial branch of "main", creating path's parent directory as needed) and immediately disables gc.auto, maintenance.auto and receive.autogc directly in it via ConfigureGitAutoMaintenanceOff.

#754's round-2 review found the same latent flake class -- a same-host `git push` can leave receive-pack's own detached `git gc --auto`/`git maintenance run --auto` still writing inside a bare fixture repo, racing t.TempDir()'s recursive removal of it at test end -- present in roughly fifteen other cmd/wb fixtures that created a bare remote directly and never called ConfigureGitAutoMaintenanceOff on it. This helper exists so every bare remote a wb test fixture creates gets that protection by construction, rather than depending on each new fixture remembering the call.

func Isolate

func Isolate(t testing.TB)

Isolate truly unsets every WB_AGENT_* variable -- the key itself is removed from the environment, not merely emptied -- and sets GOWORK=off for the current test's environment. It restores every value it changed, via t.Cleanup, when t (or the subtest it was called from) completes.

t.Setenv("WB_AGENT_X", "") is not enough: it leaves the key present with an empty value, and envguard.Inspect (like most agent-var detection) keys off presence in os.Environ(), not value, so an emptied variable still reads as set. Isolate calls os.Unsetenv directly instead.

A test that intentionally exercises agent-mode behavior calls Isolate first and then sets its own WB_AGENT_* value with t.Setenv, so the intentional value applies last and is the one the code under test observes.

Like t.Setenv, Isolate makes the calling test (and its subtests) unsafe to run in parallel with sibling tests that also touch the process environment: it mutates shared process state and only restores it on this test's own Cleanup.

func IsolateHarnessProcess added in v0.175.5

func IsolateHarnessProcess()

IsolateHarnessProcess removes every agent-harness variable from the test process, for a package's TestMain, so a test binary run from inside an agent session sees the same environment it sees on CI.

Isolate and IsolateProcess only remove WB_AGENT_*. A harness also exports its own session and process variables, and those are enough for a work-log claim made by `worktree create` to register the real harness process found by walking the test binary's parents, so that session owns the claim instead of the session the test declared. That made TestWorktreeRelocateCLIJSONEnvelopeAndShortcut report "worktree has an active owner" and TestSessionResumeLocalActualCustodyRefusalDoesNotClaimRoute report that its source session does not own the active Work Log, on every run from inside an agent session and never on CI.

It is not restored: TestMain's process exits once m.Run returns. A test that exercises harness behaviour sets its own variables with t.Setenv.

func IsolateProcess

func IsolateProcess()

IsolateProcess performs the same isolation as Isolate for a caller with no *testing.T -- typically a package's TestMain, which isolates its whole test binary process before any test runs. Unlike Isolate this is not restored: TestMain's process exits once m.Run() returns, so there is nothing to restore it for.

func IsolateUserState

func IsolateUserState() (remove func(), err error)

IsolateUserState gives the whole test process a private, empty user: HOME, XDG_CONFIG_HOME and XDG_STATE_HOME point into one fresh temporary root, and the ambient WB variables that select real state are unset. Call it from TestMain before any test runs; the returned function removes the root.

It exists because a stubbed command is not an isolated one: on 2026-10-02 a cmd/wb test that stubbed the cleanup engine still ran the command's post-apply claim release, which read the developer's real wb.yaml and released a real claim in the fleet's state repository.

Only the packages whose TestMain calls this are isolated: cmd/wb, internal/hooks, internal/lifecyclehooks and internal/hostload. Not yet isolated, and still reading the developer's own home: internal/worktrees, internal/orchestrate and every other package with tests.

Two things a test legitimately inherits survive the move. The Go toolchain's caches and settings are pinned to where they already were (derived from the environment, the GOENV file and Go's documented defaults, never by running the go command), and the private home's .gitconfig includes the developer's own global Git configuration, so Git keeps the identity and settings it had. That inclusion is deliberate and it is not neutral: the developer's credential helpers and url.insteadOf rewrites stay live, so a test that reaches a real remote does so with real credentials.

The private home holds an empty projects directory, because a wb process given no --projects-root and no WB_PROJECTS_ROOT resolves, and opens, ~/projects.

A process the test binary starts inherits all of this through its environment, and that includes the test binary re-executing itself as a helper: its TestMain calls IsolateUserState again, finds UserStateRootEnv naming a live private root, and adopts the environment it was given as it is. Isolating afresh there would discard what the parent test deliberately set for its children (a WB_PROJECTS_ROOT pointing at its fixture, say) and leave them resolving an empty home of their own. The parent owns the root, so an adopting process removes nothing.

func SetGitAutoMaintenanceOff added in v0.164.0

func SetGitAutoMaintenanceOff(t testing.TB)

SetGitAutoMaintenanceOff sets GIT_CONFIG_COUNT/KEY_N/VALUE_N for the current test's process environment (via t.Setenv, restored on Cleanup) so that every git subprocess this test starts directly -- and every subprocess of those, such as a locally spawned `git-upload-pack` -- has gc.auto, maintenance.auto and receive.autogc disabled. It extends any GIT_CONFIG_COUNT sequence already present in the process environment rather than overwriting it.

Like GitAutoMaintenanceOffEnv, this does not reach a server-side `receive-pack` a same-host `git push` spawns for a bare remote (see its doc comment); call ConfigureGitAutoMaintenanceOff on that repository directly as well.

func UserStateViolations

func UserStateViolations(extra ...string) []string

UserStateViolations lists every way the current process could still reach a real user's configuration or state. It is empty only after IsolateUserState and for as long as nothing has pointed the process back outside its root; extra are further resolved paths a package wants held to the same root.

func WriteExecutableFile added in v0.166.0

func WriteExecutableFile(path string, content []byte, perm os.FileMode) error

WriteExecutableFile re-exports execfile.WriteExecutableFile for the callers across this repository that already import testenv for other fixtures. See that package's doc comment for why the implementation lives there instead of here: internal/envguard (which this package itself imports, for Isolate) needs the same primitive in its own tests, and internal/envguard importing internal/testenv back would be a cycle.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL