# testdirs: assert against real config/cache paths, not $HOME prefix

`d1a2dd0`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·1mo ago·6 files·+32 added/-13 removed

Review feedback on #1411: the fallback uses os.MkdirTemp, which respects
TMPDIR — and TMPDIR can legitimately live under $HOME. The invariant is
"never the real ~/.config/entire or ~/.cache/entire", not "outside the
home directory". Tighten the two regression tests to assert exactly
that, and reword the doc comments to stop implying the fallback is
guaranteed to be outside $HOME.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

## Sessions

85db2d359086View transcript

[?\
Fix Test Fixture Leaking Into User ConfigClaude Code·Fable 5.[1m]·6 steps](/content/gh/entireio/cli/session/688d98e0-f0a6-4240-acff-5f1100197cba#timeline-85db2d359086/index.html)

## Changes

6

- cmd/entire/cli/versioncheck

- Mversioncheck.go+1/-1

- internal

- entireclient

- contexts

- Mcontexts.go+3/-3

- Mcontexts_test.go+10/-4

- discovery

- Mcache.go+2/-1

- testdirs

- Mtestdirs.go+4/-1

- Mtestdirs_test.go+12/-3

```
101 unmodified lines

102
103
104
105
105
106
107
108

101 unmodified lines

// globalConfigDirPath returns the CLI's global config directory:
// $ENTIRE_CONFIG_DIR if set, else ~/.config/entire. Under `go test` an
// unset ENTIRE_CONFIG_DIR resolves to a throwaway per-process directory
// instead of the real home (see internal/testdirs).
// instead of the real ~/.config/entire (see internal/testdirs).
func globalConfigDirPath() (string, error) {
	if dir := os.Getenv("ENTIRE_CONFIG_DIR"); dir != "" {
		return dir, nil
```

Mcmd/entire/cli/versioncheck/versioncheck.go+1/-1

```
66 unmodified lines

67
68
69
70
71
72
70
71
72
73
74
75

66 unmodified lines

// DefaultConfigDir is $ENTIRE_CONFIG_DIR if set, else ~/.config/entire.
// Under `go test` an unset ENTIRE_CONFIG_DIR resolves to a throwaway
// per-process directory instead of the real home, so a test that forgets to
// isolate can never read or pollute the developer's real config (see
// internal/testdirs).
// per-process directory instead of the real ~/.config/entire, so a test that
// forgets to isolate can never read or pollute the developer's real config
// (see internal/testdirs).
func DefaultConfigDir() string {
	if dir := os.Getenv("ENTIRE_CONFIG_DIR"); dir != "" {
		return dir
```

Minternal/entireclient/contexts/contexts.go+3/-3

```
292 unmodified lines

293
294
295
296
296
297
298
299
299
300
301
302
303
304
305
306
307
306
307
308
309
310
311
312
313
314
315

292 unmodified lines

}
}

func TestDefaultConfigDir_TestRunsNeverResolveRealHome(t *testing.T) {
func TestDefaultConfigDir_TestRunsNeverResolveRealConfigDir(t *testing.T) {
	// With no explicit override, a `go test` process must fall back to a
	// throwaway directory — never ~/.config/entire, where it could read or
	// pollute the developer's real contexts.json.
	// pollute the developer's real contexts.json. (The fallback lives under
	// os.TempDir, which may itself be under $HOME via TMPDIR — that's fine;
	// only the real config location is off-limits.)
	t.Setenv("ENTIRE_CONFIG_DIR", "")
	got := contexts.DefaultConfigDir()
	if got == "" {
		 t.Fatal("DefaultConfigDir returned empty string")
	}
	home, err := os.UserHomeDir()
	if err == nil && (got == home || strings.HasPrefix(got, home+string(os.PathSeparator))) {
		 t.Fatalf("DefaultConfigDir = %q resolves under the real home %q during tests", got, home)
	}
	if err != nil {
		 t.Skipf("no home dir: %v", err)
	}
	realDir := filepath.Join(home, ".config", "entire")
	if got == realDir || strings.HasPrefix(got, realDir+string(os.PathSeparator)) {
		 t.Fatalf("DefaultConfigDir = %q resolves to the real config dir %q during tests", got, realDir)
	}
}
```

Minternal/entireclient/contexts/contexts_test.go+10/-4

```
40 unmodified lines

41
42
43
44
44
45
46
47
48

40 unmodified lines

// DefaultCacheDir returns ~/.cache/entire, respecting XDG_CACHE_HOME.
// Under `go test` an unset XDG_CACHE_HOME resolves to a throwaway
// per-process directory instead of the real home (see internal/testdirs).
// per-process directory instead of the real ~/.cache/entire (see
// internal/testdirs).
func DefaultCacheDir() string {
	if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
		return filepath.Join(xdg, "entire")
```

Minternal/entireclient/discovery/cache.go+2/-1

```
7 unmodified lines

8
9
10
11
11
12
13
14
15
16
17

7 unmodified lines

// ENTIRE_TOKEN_STORE, ...) would silently read and write the developer's
// real configuration — real auth contexts have been polluted with test
// fixtures this way. Dir gives the resolution functions a safe default under
// test: a throwaway directory under os.TempDir instead of the real home.
// test: a throwaway directory under os.TempDir, never the real config/cache
// locations. (os.TempDir respects TMPDIR and may itself live under $HOME;
// the guarantee is "not ~/.config/entire or ~/.cache/entire", not "outside
// the home directory".
//
// The fallback directory is per-process, not per-test: all tests in one test
// binary share it. Tests that need isolation from each other must still set
```

Minternal/testdirs/testdirs.go+4/-1

```
1 unmodified line

2
3
4
5
6
7
8
18 unmodified lines

27
28
29
30
31
32
33
30
31
32
34
35
36
37
38
39
40
41
42
43
44

1 unmodified line

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)
18 unmodified lines

t.Fatalf("surfaces must not share a directory: %q", cache)

// The invariant is "never the real config/cache locations" — not "never
	// under $HOME": os.MkdirTemp respects TMPDIR, which may itself live under
	// the home directory on some setups.
	if home, err := os.UserHomeDir(); err == nil {
		for _, d := range []string{cfg1, cache} {
			if strings.HasPrefix(d, home+string(os.PathSeparator)) || d == home {
				t.Fatalf("fallback dir %q resolves under the real home %q", d, home)
			}
		}
		for _, realDir := range []string{
			filepath.Join(home, ".config"),
			filepath.Join(home, ".cache"),
		} {
			for _, d := range []string{cfg1, cache} {
				if d == realDir || strings.HasPrefix(d, realDir+string(os.PathSeparator)) {
					t.Fatalf("fallback dir %q resolves under the real %q", d, realDir)
				}
			}
		}
	}
```

Minternal/testdirs/testdirs_test.go+12/-3
